In this notebook, I have created a step-by-step documentation of the analysis, and I will try to comment as much as possible to make clear what I have analysed and what I think could be important results that might indicate what biology is behind the expression differences we observe.

We established that sample 47 was undefined and non-sensical in our previous analysis and is thus omitted from this analysis. Here we take the recent data (50,51,52) to analyse and understand if ablation shows a identifyable effect.

Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'spatstat.core':
  method          from
  formula.glmmPQL MASS
Attaching SeuratObject
Attaching sp

Now we perform QC, looking at the percentage of mitochondrial RNA vs other RNA, plus other metrics.
* nFeature_RNA = number of genes
* nCount_RNA = number of UMIs or Counts
* percent.mt = percent of expression of mitochondrial genes versus the rest

# The [[ operator can add columns to object metadata. This is a great place to stash QC stats
oligos[["percent.mt"]] <- PercentageFeatureSet(oligos, pattern = "^mt-")
# Visualize QC metrics as a violin plot
VlnPlot(oligos, group.by = "Sample",features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 1,pt.size = 0.1)

The samples seem to differ QC-wise, and many samples show a wide spread for the percent of mitochondrial expression, requiring a stringent cut-off. Now we plot the QC information in another way to see if we can estimate thesholds for removing bad cells and perhaps doublets.

# FeatureScatter is typically used to visualize feature-feature relationships, but can be used
# for anything calculated by the object, i.e. columns in object metadata, PC scores etc.
plot1 <- FeatureScatter(oligos, group.by = "Sample",feature1 = "nCount_RNA", feature2 = "percent.mt",pt.size = 0.5)
plot2 <- FeatureScatter(oligos, group.by = "Sample", feature1 = "nCount_RNA", feature2 = "nFeature_RNA",pt.size = 0.5)
CombinePlots(plots = list(plot1, plot2))
Warning: CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

These samples seem to be performing differently, and we have very high percent of mitochondrial genes especially in the TC_50 sample, which concurrently is also lower in number of genes expressed and number of UMIs detected. Now we will remove cells expressing less that 200 genes (to remove bad cells),
and more than 3000 genes (to remove doublets). And remove cells expressing more that 5% mitochondrial genes. And replot the QC data.

Just to alleviate any concerns, the downstream analysis does not seem to be significantly changed with more stringent or loosened cut-offs, in terms of DTA significant genes.

#Clean up the data
oligos <- subset(oligos, subset = nFeature_RNA > 200 & nFeature_RNA < 3000 & percent.mt < 15)
ncol(oligos)
[1] 9291

Now we look at the cleaned-up data.

# The [[ operator can add columns to object metadata. This is a great place to stash QC stats
oligos[["percent.mt"]] <- PercentageFeatureSet(oligos, pattern = "^mt-")
# Visualize QC metrics as a violin plot
VlnPlot(oligos, group.by = "Sample",features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 1,pt.size = 0.1)

# FeatureScatter is typically used to visualize feature-feature relationships, but can be used
# for anything calculated by the object, i.e. columns in object metadata, PC scores etc.
plot1 <- FeatureScatter(oligos, group.by = "Sample",feature1 = "nCount_RNA", feature2 = "percent.mt",pt.size = 0.5)
plot2 <- FeatureScatter(oligos, group.by = "Sample", feature1 = "nCount_RNA", feature2 = "nFeature_RNA",pt.size = 0.5)
CombinePlots(plots = list(plot1, plot2))
Warning: CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

Now we normalize the dataset.

Generating the UMAP and TSNE.

oligos.integrated <- RunUMAP(oligos.integrated, dims = 1:30)
13:25:46 UMAP embedding parameters a = 0.9922 b = 1.112
13:25:46 Read 9291 rows and found 30 numeric columns
13:25:46 Using Annoy for neighbor search, n_neighbors = 30
13:25:46 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:25:46 Writing NN index file to temp file /var/folders/wx/df1rnvs15s5434nztss_v4z00000gn/T//Rtmpz45HdB/fileaacd2c0c2ab6
13:25:46 Searching Annoy index using 1 thread, search_k = 3000
13:25:48 Annoy recall = 100%
13:25:48 Commencing smooth kNN distance calibration using 1 thread
13:25:49 Initializing from normalized Laplacian + noise
13:25:49 Commencing optimization for 500 epochs, with 390252 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:26:04 Optimization finished
oligos.integrated <- RunTSNE(oligos.integrated, dims = 1:30)
plots <- DimPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
Warning: CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

plots <- TSNEPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
Warning: CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

Both the UMAP and tSNE show that the DTA and the Control are separated. I will now perform clustering as normal, then I will follow this up by integrating the data with the previous data from the Science paper to show how these clusters relate to the original papers clusters.

Now we plot show expression of some common genes found previously to be stable clusters within the mouse OLs, just for reference.

DefaultAssay(oligos.integrated) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
#FeaturePlot(oligos.integrated, c("Tmsb4x","Tpt1","Fth1","Enpp2","App"),pt.size = 0.1)
FeaturePlot(oligos.integrated, c("Pdgfra", "Ptprz1","Bmp4","Itpr2", "Egr1", "Klk6", "Hopx", "Ptgds","Il33"),pt.size = 0.1)

DefaultAssay(oligos.integrated) <- "SCT"

As you can see, most of the OL cluster seems to be Ptgds/Il33 postitive instead of Hopx/Klk6 meaning that we might have mostly MOL5/6 of the original cluster.

I show the clusters on the UMAP so you can see their position.

oligos.integrated <- FindNeighbors(oligos.integrated, dims = 1:30,nn.method="rann")
Computing nearest neighbor graph
Computing SNN
oligos.integrated <- FindClusters(oligos.integrated,algorithm = 4,resolution = 0.6)
DimPlot(oligos.integrated, group.by = c("seurat_clusters"), combine = FALSE)
[[1]]

DimPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
[[1]]

oligos.integrated$seurat_clusters_rn <- factor(oligos.integrated$seurat_clusters, levels= c(
  "4",
  "11",
  "10",
  "9",
  "7",
  "6",
  "8",
  "2",
  "1",
  "5",
  "3"))

library(plyr)
oligos.integrated$seurat_clusters_rn  <- revalue(as.factor(oligos.integrated$seurat_clusters_rn), c("4"="OPC","11"="OPC cycling","10"="COP/NFOL/MFOLa","9"="MFOLb","7"="MOL1","6"="MOL2a","8"="MOL2b","2"="MOL5/6a","1"="MOL5/6b","5"="MOL5/6c","3"="MOL5/6d"))
DimPlot(oligos.integrated, group.by = c("seurat_clusters_rn"), combine = FALSE,label=T)
[[1]]

DimPlot(oligos.integrated, group.by = c("seurat_clusters"), combine = FALSE)
[[1]]

oligos.integrated.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)

Below follows the heatmap showing the top 10 genes based on fold change for each cluster.

DefaultAssay(oligos.integrated) <- "SCT"
Idents(oligos.integrated) <- oligos.integrated@meta.data$seurat_clusters_rn
top10 <- oligos.integrated.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
DoHeatmap(oligos.integrated, features = top10$gene) + NoLegend()
Idents(oligos.integrated) <- oligos.integrated@meta.data$seurat_clusters

And here are the top 2 genes found for each cluster as shown on the UMAP.

DefaultAssay(oligos.integrated) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
top2 <- oligos.integrated.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
FeaturePlot(oligos.integrated, features = top2$gene,pt.size = 0.1)
DefaultAssay(oligos.integrated) <- "SCT"

Here I show a table with the DTA relating to the Seurat found clusters. TRUE means Ablated

condition <- oligos.integrated@meta.data$Sample
condition <- strsplit(condition,"_")
Ablated <- as.logical()
for (i in 1:length(condition)) {
Ablated <-   c(Ablated,"GD"==condition[[i]][1])
}
condition <- Ablated
Ablated <- as.data.frame(Ablated)
row.names(Ablated) <- colnames(oligos.integrated)
oligos.integrated <- AddMetaData(oligos.integrated,Ablated)
table(oligos.integrated$seurat_clusters,oligos.integrated$Ablated)

Label transfer

Now we attempt to transfer the cluster labels of the Science dataset onto the 10X dataset.

DimPlot(oligos.integrated, group.by = c("seurat_clusters"), combine = FALSE)
DimPlot(oligos.integrated, group.by = c("predicted.id"), combine = FALSE)
DimPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)

As you can see the Science paper clusters are showing what we could determine with the markers as well, most if not all of the DTA effect of the MOLs is located into a single OL population - MOL5. OPCs ofcourse also have a DTA effect.

table(oligos.integrated$predicted.id,oligos.integrated$Ablated)
data <- as.data.frame(table(oligos.integrated$Sample,oligos.integrated$predicted.id))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
#data$Cluster  <- revalue(as.factor(data$Cluster),c("PPR"="VLMC"))
# Stacked + percent
ggplot(data, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")
ggplot(data, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar( stat="identity") + scale_y_log10()

data <- as.data.frame(table(oligos.integrated$Sample,oligos.integrated$seurat_clusters))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
#data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
#data$Cluster  <- revalue(as.factor(data$Cluster),c("PPR"="VLMC"))
# Stacked + percent
ggplot(data, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")
ggplot(data, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar( stat="identity")

data <- as.data.frame(table(oligos.integrated$Sample,oligos.integrated$predicted.id))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
library(reshape2)
datacasted <- dcast(data,Cluster ~ Condition)
calc_cpm <-function (expr_mat) 
{
    norm_factor <- colSums(expr_mat)
    return(t(t(expr_mat)/norm_factor)) * 10^50
}
datacasted[,2:4] <- calc_cpm(datacasted[,2:4])
data <- melt(datacasted)
colnames(data) <- c("Condition","Cluster","Freq")
#data$Cluster  <- revalue(as.factor(data$Cluster),c("PPR"="VLMC"))
# Stacked + percent
ggplot(data, aes(fill=Cluster, y=Freq, x=Condition)) + 
    geom_bar(position="fill", stat="identity")
ggplot(data, aes(fill=Cluster, y=Freq, x=Condition)) + 
    geom_bar( stat="identity")
library(heatmap3)
library(viridis)
comparison <-scale(t(scale(table(oligos.integrated$Sample,oligos.integrated$seurat_clusters))))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=colorRampPalette(c("limegreen","black",
"firebrick3"))(1024),balanceColor =T,cexRow = 2,cexCol = 2,margins = c(10, 10))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=viridis(1000),balanceColor =T,cexRow = 2,cexCol = 2,margins = c(10, 10))

library(heatmap3)
library(viridis)
ClustersScience  <- factor(oligos.integrated$predicted.id,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
comparison <-scale(t(scale(table(oligos.integrated$Sample,ClustersScience))))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=colorRampPalette(c("limegreen","black",
"firebrick3"))(1024),balanceColor =T,cexRow = 2,cexCol = 2,margins = c(10, 10))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=viridis(1000),balanceColor =F,cexRow = 2,cexCol = 2,margins = c(10, 10))

library(heatmap3)
library(viridis)
ClustersScience  <- factor(oligos.integrated$predicted.id,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
comparison <-t(scale(t(scale(table(ClustersScience,oligos.integrated$Sample)))))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=colorRampPalette(c("limegreen","black",
"firebrick3"))(1024),balanceColor =T,cexRow = 2,cexCol = 2,margins = c(10, 10))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=viridis(1000),balanceColor =F,cexRow = 2,cexCol = 2,margins = c(10, 10))

data <- as.data.frame(table(oligos.integrated$Sample,oligos.integrated$predicted.id))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6","PPR"))
library(reshape2)
datacasted <- dcast(data,Cluster ~ Condition)
calc_cpm <-function (expr_mat) 
{
    norm_factor <- colSums(expr_mat)
    return(t(t(expr_mat)/norm_factor))
}
datacasted[,2:4] <- calc_cpm(datacasted[,2:4])*100
row.names(datacasted) <- datacasted[,1]
datacasted <- datacasted[,2:4]
comparison <-t(scale(t(datacasted)))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=viridis(1000),balanceColor =F,cexRow = 2,cexCol = 2,margins = c(10, 10))
comparison <-datacasted-apply(datacasted,1,function(x) mean(x))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=viridis(1000),balanceColor =F,cexRow = 2,cexCol = 2,margins = c(10, 10))
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=colorRampPalette(c("limegreen","black",
"firebrick3"))(1024),balanceColor =T,cexRow = 2,cexCol = 2,margins = c(10, 10))
data <- as.data.frame(table(oligos.integrated$Sample,oligos.integrated$seurat_clusters_rn))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","NFOL2","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6","PPR"))
library(reshape2)
datacasted <- dcast(data,Cluster ~ Condition)
calc_cpm <-function (expr_mat) 
{
    norm_factor <- colSums(expr_mat)
    return(t(t(expr_mat)/norm_factor))
}
datacasted[,2:4] <- calc_cpm(datacasted[,2:4])*100
row.names(datacasted) <- datacasted[,1]
datacasted <- datacasted[,2:4]
datamelted <- melt(t(datacasted))
datamelted$Var2 <- as.factor(datamelted$Var2)
ggplot(datamelted, aes(y = value, x = Var2)) + # Move y and x here so than they can be used in stat_*
    geom_dotplot(aes(fill = Var1),   # Use fill = Species here not in ggplot()
                 binaxis = "y",         # which axis to bin along
                 binwidth = 1.25,        # Minimal difference considered diffeerent
                 stackdir = "center",
                 position = position_jitter(0.2)# Centered
                 ) +  scale_fill_manual(values=c("#70BF45","#5675D6","#C9502B"))+# scale_y_log10() + 
    stat_summary(fun.y = mean, fun.ymin = mean, fun.ymax = mean,
                 geom = "crossbar", width = 0.5,fatten = 0.01) + theme(axis.text.x = element_text(angle = 45))

To establish what genes are shifted/upregulated/downregulated, I perform a tour de force (just pressing a button) to calculate for each individual population (not including clusters with very few ablated cells)

OPC COP MFOL2 MOL1 MOL2 MOL3 MOL4 MOL5 MOL6

I employ adjusted p-values, so low numbers of cells will not show significance, especially as low effect sizes in those populations.

The dashed line indicates the threshold for p<0.01

library(ggrepel)
SetIdent(oligos.integrated,value=oligos.integrated@meta.data$predicted.id)
Idents(oligos.integrated) <- oligos.integrated@meta.data$predicted.id
oligos.integrated$cluster <- oligos.integrated$predicted.id
oligos.integrated$celltype.sample <- paste(Idents(oligos.integrated), oligos.integrated$Ablated, sep = "_")
oligos.integrated$celltype <- Idents(oligos.integrated)
Idents(oligos.integrated) <- "celltype.sample"
table(Idents(oligos.integrated))

DefaultAssay(oligos.integrated) <- "SCT"

oligos.integrated.samplediffOPCRNA <- FindMarkers(oligos.integrated, ident.1 = "OPC_TRUE", ident.2 = "OPC_FALSE", verbose = FALSE,logfc.threshold = 0.1,min.pct=0)
#head(oligos.integrated.samplediffOPCRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffOPCRNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj+1e-300)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point(size=0.2)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 1e-30 & abs(avg_logFC) > 0),                              label=row.names(subset(diffmatrix, p_val_adj < 1e-30 & abs(avg_logFC) > 0)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffCOPRNA <- FindMarkers(oligos.integrated, ident.1 = "COP_TRUE", ident.2 = "COP_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffCOPRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffCOPRNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffMFOL2RNA <- FindMarkers(oligos.integrated, ident.1 = "MFOL2_TRUE", ident.2 = "MFOL2_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffMFOL2RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMFOL2RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffMOL1RNA <- FindMarkers(oligos.integrated, ident.1 = "MOL1_TRUE", ident.2 = "MOL1_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffMOL1RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMOL1RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5)

oligos.integrated.samplediffMOL2RNA <- FindMarkers(oligos.integrated, ident.1 = "MOL2_TRUE", ident.2 = "MOL2_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffMOL2RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMOL2RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffMOL3RNA <- FindMarkers(oligos.integrated, ident.1 = "MOL3_TRUE", ident.2 = "MOL3_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffMOL3RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMOL3RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffMOL4RNA <- FindMarkers(oligos.integrated, ident.1 = "MOL4_TRUE", ident.2 = "MOL4_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffMOL4RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMOL4RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5)

oligos.integrated.samplediffMOL5RNA <- FindMarkers(oligos.integrated, ident.1 = c("MOL5_TRUE","MOL6_TRUE"), ident.2 = c("MOL5_FALSE","MOL6_FALSE"), verbose = FALSE,logfc.threshold = 0.1,min.pct=0)
#head(oligos.integrated.samplediffMOL5RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMOL5RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5)

oligos.integrated.samplediffMOL6RNA <- FindMarkers(oligos.integrated, ident.1 = "MOL6_TRUE", ident.2 = "MOL6_FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffMOL6RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffMOL6RNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.3)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5)

Idents(oligos.integrated) <- "Ablated"
oligos.integrated.samplediffAllRNA <- FindMarkers(oligos.integrated, ident.1 = "TRUE", ident.2 = "FALSE", verbose = FALSE)
#head(oligos.integrated.samplediffAllRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffAllRNA
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.35),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.35)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 
#venndiagram
library(VennDiagram)

As you can see from the plots, MOL5 takes the lions share of the DTA effect, which seems to be concentrated in the MOL5 and MOL3 clusters. MOL3 is interestingly predicted in cells very close to the DTA clusters in the UMAP.

The other major part of the DTA effect is located in the OPC cluster.

Finally, the last plot is a differential expression result of all the cells, and as you can see MOLs share many genes in the effect, but OPC shows slightly different genes.

To illustrate these effects, I will calculate the top 20 genes of the DTA effect across all cells and see how the major differentially expressed genes translate to the clusters/UMAP position.

top5 <- row.names(head(oligos.integrated.samplediffAllRNA,20))

DefaultAssay(oligos.integrated) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
top5 <- row.names(head(oligos.integrated.samplediffAllRNA,20))
FeaturePlot(oligos.integrated, features = top5,pt.size = 0.1,ncol = 2)
DefaultAssay(oligos.integrated) <- "SCT"

And here are violinplots of the same genes but now organised per cluster and ablation condition. Ablated = TRUE

DefaultAssay(oligos.integrated) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)

# oligosintegrated.list <- SplitObject(oligos.integrated, split.by = "Ablated")
# oligos.integrated.cc <- merge(oligosintegrated.list[["FALSE"]], y = oligosintegrated.list[["TRUE"]], merge.data = TRUE)
plots <- VlnPlot(oligos.integrated, features = top5, split.by = "Ablated", group.by = "celltype", 
    pt.size = 0, combine = FALSE)
CombinePlots(plots = plots, ncol = 1)
#rm(oligosintegrated.list)

DefaultAssay(oligos.integrated) <- "SCT"

Now that this is done, it’s clear the OLs and OPCs have some differences regarding to the genes affected by ablation. Now, to get any sort of meaningfull insight into the DTA effect, it seems logical to separate the effect by cluster to avoid MOL-state expression to influence the analysis.

If you remember MOL5 is the most affected of the MOLs or in any case the most present in the dataset. Furthermore, the DTA effect seemed to be comparable between the OL clusters, therefore to focus completely on the DTA effect I will only focus on MOL5, because it has many differentially expressed genes.

Here I plot the clusters again for reference.

DimPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
DimPlot(oligos.integrated, group.by = c("seurat_clusters"), combine = FALSE)
DimPlot(oligos.integrated, group.by = c("predicted.id"), combine = FALSE)

If you remember from the Seurat clustering, MOL5 is subclustered into 4 clusters. For the analysis to proceed I will make the assumption that we have healthy and affected states of MOL5 captured in the Seurat clusters.

Furthermore, it seems that we have two distinct DTA states reached and that we can divide MOL5 into two sides (as the UMAP is suggesting). Here I plot 3 genes that show that MOL5 has heterogenous expression within the clusters broadly dividing the cluster in two (seemingly regardless of DTA condition).

So, therefore I make a second assumption that Seurat clusters 2 and 1 represent a pair of MOL5 subclusters that represent the closest states to each other, most likely representing two sides of the same coin, and Seurat clusters 3 and 5 represent another pair of clusters closely related to each other.

Therefore it would make sense to compare Seurat cluster 2 vs 1 and 3 vs 5 and treat them as 2 diffent biological processes that are altered following ablation.

Here I plot the genes showing MOL5 can be divided into roughly two clusters.

DefaultAssay(oligos.integrated) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
FeaturePlot(oligos.integrated, c("Apod", "Tmsb4x","Tppp3"),pt.size = 1)
DefaultAssay(oligos.integrated) <- "SCT"

Here I intersect the results of a couple of clusters to get some shared and non-shared genes differentially expressed regarding the ablation. Showing adjusted p-values.

The take-away should be the first two plots,

Then we have

OPC_diff <- subset(oligos.integrated.samplediffOPCRNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)
MOL5_diff <- subset(oligos.integrated.samplediffMOL5RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)
MOL4_diff <- subset(oligos.integrated.samplediffMOL4RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)
MOL3_diff <- subset(oligos.integrated.samplediffMOL3RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)

OPC_MOL5shared <- intersect(row.names(OPC_diff),row.names(MOL5_diff))
OPC_MOL5nonshared <- setdiff(row.names(OPC_diff),row.names(MOL5_diff))
OPC_onlycomparedtoMOL5<- row.names(OPC_diff)[!row.names(OPC_diff) %in% row.names(MOL5_diff)]
MOL5_onlycomparedtoOPC<- row.names(MOL5_diff)[!row.names(MOL5_diff) %in% row.names(OPC_diff)]
MOL5_4shared <- intersect(row.names(MOL4_diff),row.names(MOL5_diff))
MOL5_3shared<- intersect(row.names(MOL3_diff),row.names(MOL5_diff))
MOL5_4_3shared <- intersect(row.names(MOL3_diff),intersect(row.names(MOL4_diff),row.names(MOL5_diff)))
OPC_MOL5shared
OPC_onlycomparedtoMOL5
MOL5_onlycomparedtoOPC
View(c(MOL5_onlycomparedtoOPC,OPC_MOL5shared))
View(c(OPC_onlycomparedtoMOL5,OPC_MOL5shared))
View(c(MOL5_4shared,OPC_MOL5shared))

diffmatrix <- MOL5_diff[MOL5_onlycomparedtoOPC,]
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

diffmatrix <- OPC_diff[OPC_onlycomparedtoMOL5,]
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5)

diffmatrix <- MOL5_diff[OPC_MOL5shared,]
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

diffmatrix <- OPC_diff[OPC_MOL5shared,]
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

diffmatrix <- MOL5_diff[MOL5_3shared,]
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

I made the second assumption that Seurat clusters 2 and 1 represent a pair of MOL5 subclusters that represent the closest states to each other, most likely representing two sides of the same coin, and Seurat clusters 3 and 5 represent another pair of clusters closely related to each other.

Therefore it would make sense to compare Seurat cluster 2 vs 1 and 3 vs 5 and treat them as 2 diffent biological processes that are altered following ablation. Which I do here:

library(ggrepel)
SetIdent(oligos.integrated,value=oligos.integrated@meta.data$seurat_clusters)
Idents(oligos.integrated) <- oligos.integrated@meta.data$seurat_clusters
oligos.integrated$cluster <- oligos.integrated$seurat_clusters
oligos.integrated$celltype.sample <- paste(Idents(oligos.integrated), oligos.integrated$Ablated, sep = "_")
oligos.integrated$celltype <- Idents(oligos.integrated)
#Idents(oligos.integrated) <- "celltype.sample"
#table(Idents(oligos.integrated))

DefaultAssay(oligos.integrated) <- "SCT"
oligos.integrated.samplediffDTA2vs1RNA <- FindMarkers(oligos.integrated, ident.1 = "2", ident.2 = c("1","5"), verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffDTA2vs1RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffDTA2vs1RNA
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=log_p_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, log_p_val > 100 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, log_p_val > 100 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8) 

#View(subset(oligos.integrated.samplediffDTA2vs1RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25))

oligos.integrated.samplediffDTA3vs5RNA <- FindMarkers(oligos.integrated, ident.1 = "3", ident.2 = c("1","5"), verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffDTA3vs5RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffDTA3vs5RNA
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=log_p_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.45),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.45)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8) 

oligos.integrated.samplediffDTA2vs3RNA <- FindMarkers(oligos.integrated, ident.1 = "2", ident.2 = "3", verbose = FALSE,logfc.threshold = 0.25,min.pct=0.1)
#head(oligos.integrated.samplediffDTA3vs5RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffDTA2vs3RNA
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj+1e-300)
ggplot(diffmatrix,aes(avg_logFC,y=log_p_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 1e-200 & abs(avg_logFC) > 0),                              label=row.names(subset(diffmatrix, p_val_adj < 1e-200 & abs(avg_logFC) > 0)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8)

oligos.integrated.samplediffDTA23vs15RNA <- FindMarkers(oligos.integrated, ident.1 = c("2","3"), ident.2 = c("1","5"), verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffDTA3vs5RNA, n = 50)
diffmatrix <- oligos.integrated.samplediffDTA23vs15RNA
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj+1e-300)
ggplot(diffmatrix,aes(avg_logFC,y=log_p_val,label=row.names(diffmatrix)))+ geom_point(size=0.2)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 1e-100 & abs(avg_logFC) > 0),                              label=row.names(subset(diffmatrix, p_val_adj < 1e-100 & abs(avg_logFC) > 0)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8)

#View(subset(oligos.integrated.samplediffDTA3vs5RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25))
DTA_diff <- subset(oligos.integrated.samplediffDTA23vs15RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)
DTA_diff_UL <- subset(oligos.integrated.samplediffDTA2vs3RNA,row.names(oligos.integrated.samplediffDTA2vs3RNA) %in% row.names(DTA_diff)) 
                      
diffmatrix <- DTA_diff_UL
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj+1e-300)
ggplot(diffmatrix,aes(avg_logFC,y=log_p_val,label=row.names(diffmatrix)))+ geom_point(size=0.2)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 1e-30 & abs(avg_logFC) > 0),                              label=row.names(subset(diffmatrix, p_val_adj < 1e-30 & abs(avg_logFC) > 0)))+xlab("log2_FC") + ylab("-log10_p-value") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8)

Idents(oligos.integrated) <- "Ablated"
oligos.integrated.samplediffAllRNA <- FindMarkers(oligos.integrated, ident.1 = "TRUE", ident.2 = "FALSE", verbose = FALSE)
head(oligos.integrated.samplediffAllRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffAllRNA
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=log_p_val,label=row.names(diffmatrix)))+ geom_point()+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 
DiffMatrix <- list()

diffmatrixnames <- c("oligos.integrated.samplediffDTA2vs3RNA",
                    "oligos.integrated.samplediffDTA23vs15RNA")
                     

do.call(head,as.list(as.name(diffmatrixnames[1])))
library(clusterProfiler)
#Convert to gencode using biomart
library(biomaRt)
listMarts()
ensembl = useMart("ensembl",dataset="mmusculus_gene_ensembl")
listDatasets(ensembl)
attributes = listAttributes(ensembl)
Biomart_gencode_ensembl84_biotypes <- getBM(attributes=c("mgi_symbol","ensembl_gene_id","entrezgene_id","gene_biotype"), filters = "", values = "", ensembl)
Biomart_gencode_ensembl84_biotypes[, 'gene_biotype'] <- as.factor(Biomart_gencode_ensembl84_biotypes[,'gene_biotype'])
#Filter for only our genes
 Biotype_All_dataset <- subset(Biomart_gencode_ensembl84_biotypes, mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezID <-  subset(Biotype_All_dataset, Biotype_All_dataset$mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
library(ReactomePA)
library(org.Mm.eg.db)
ReactomeTerms <- list()
i=1
#UP
pvaladj <- 0.01
logfc <- 0.2
for(i in 1:length(diffmatrixnames)){
diffmatrix <- do.call("as.data.frame",as.list(as.name(diffmatrixnames[i])))
diffmatrix <- subset(diffmatrix, p_val_adj < pvaladj & avg_logFC > logfc)
#siggenes <- head(row.names(diffmatrix),100)
siggenes <- row.names(diffmatrix)
entrezmatched <- entrezID[entrezID$mgi_symbol %in% siggenes,]
#entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
modulesReactome <- enrichPathway(gene=allLLIDs,organism="mouse",pvalueCutoff=0.05,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
#modulesReactome <- enrichGO(gene=allLLIDs,"org.Mm.eg.db",pvalueCutoff=0.05,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
ReactomeTerms[[i]] <- modulesReactome
head(as.data.frame(modulesReactome))
print(i)
}
ReactomeTerms[which(lapply(ReactomeTerms,function(x) is.null(x))==TRUE)] <- "No_Genes"

#Add DOWN 
pvaladj <- 0.01
logfc <- -0.25
offset <- length(ReactomeTerms)
for(i in 1:length(diffmatrixnames)){
  i=i+offset
diffmatrix <- do.call("as.data.frame",as.list(as.name(diffmatrixnames[i-offset])))
diffmatrix <- subset(diffmatrix, p_val_adj < pvaladj & avg_logFC < logfc)
#siggenes <- head(row.names(diffmatrix),100)
siggenes <- row.names(diffmatrix)
entrezmatched <- entrezID[entrezID$mgi_symbol %in% siggenes,]
#entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
modulesReactome <- enrichPathway(gene=allLLIDs,organism="mouse",pvalueCutoff=0.05,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
#modulesReactome <- enrichGO(gene=allLLIDs,"org.Mm.eg.db",pvalueCutoff=0.05,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
ReactomeTerms[[i]] <- modulesReactome
head(as.data.frame(modulesReactome))
print(i)
}
ReactomeTerms[which(lapply(ReactomeTerms,function(x) is.null(x))==TRUE)] <- "No_Genes"
Upper_diff <- subset(oligos.integrated.samplediffDTA2vs3RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0)
Lower_diff <- subset(oligos.integrated.samplediffDTA23vs15RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0)
AlldiffgenesHetMOL5 <- intersect(intersect(row.names(oligos.integrated.samplediffDTA2vs3RNA),row.names(oligos.integrated.samplediffDTA23vs15RNA)),unique(c(row.names(Upper_diff),row.names(Lower_diff))))
subset2 <- oligos.integrated.samplediffDTA2vs3RNA[AlldiffgenesHetMOL5,]
subset3 <- oligos.integrated.samplediffDTA23vs15RNA[AlldiffgenesHetMOL5,]
subsetMOL5 <- cbind(subset2,subset3)
colnames(subsetMOL5) <- make.unique(colnames(subsetMOL5))
diffmatrix <- subsetMOL5
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj+(1e-300))
q95pgenes1 <- row.names(diffmatrix[which(diffmatrix$log_p_val >= quantile(diffmatrix$log_p_val,0)),])
diffmatrix$log_p_val.1 <- -log10(diffmatrix$p_val_adj.1+(1e-300))
q95pgenes2 <- row.names(diffmatrix[which(diffmatrix$log_p_val.1 >= quantile(diffmatrix$log_p_val.1,0)),])
q95pgenes <- unique(c(q95pgenes1,q95pgenes2))
diffmatrix <- diffmatrix[q95pgenes,]
diffmatrix$avg_logFC[is.infinite(diffmatrix$avg_logFC)] <- max(diffmatrix$avg_logFC[!is.infinite(diffmatrix$avg_logFC)])
diffmatrix$avg_logFC.1[is.infinite(diffmatrix$avg_logFC.1)] <- max(diffmatrix$avg_logFC.1[!is.infinite(diffmatrix$avg_logFC.1)])
#diffmatrix$avg_logFC.1 <- 2*diffmatrix$avg_logFC.1
diffmatrix$combp <- -log10(diffmatrix$p_val_adj*diffmatrix$p_val_adj.1)
diffmatrix$maxp <- apply(cbind(diffmatrix$log_p_val,diffmatrix$log_p_val.1),1,function(x) max(x))
diffmatrix$minp <- apply(cbind(diffmatrix$p_val_adj,diffmatrix$p_val_adj.1),1,function(x) min(x))
diffmatrix$maxp[is.infinite(diffmatrix$maxp)] <- max(diffmatrix$maxp[!is.infinite(diffmatrix$maxp)])
diffmatrix$maxFC <- apply(cbind(diffmatrix$avg_logFC,diffmatrix$avg_logFC.1),1,function(x) max(abs(x))) 
diffmatrix$Genes <- factor(row.names(diffmatrix),levels=row.names(diffmatrix))

ggplot(diffmatrix,aes(avg_logFC,y=avg_logFC.1,colour=maxp,label=row.names(diffmatrix)))+ geom_point(size=diffmatrix$maxp/200) + scale_colour_viridis_c(direction = +1,option = "viridis") + geom_hline(yintercept= 0,linetype="dashed",size=0.1,color="white")+
  geom_hline(yintercept= 0.25,linetype="dashed",size=0.1,color="white",alpha=0.5)+
  geom_hline(yintercept= -0.25,linetype="dashed",size=0.1,color="white",alpha=0.5)+
  geom_vline(xintercept= 0,linetype="dashed",size=0.1,color="white")+
  geom_vline(xintercept= 0.25,linetype="dashed",size=0.1,color="white",alpha=0.5)+
  geom_vline(xintercept= -0.25,linetype="dashed",size=0.1,color="white",alpha=0.5)+
  geom_text_repel(size=2.5,fontface = "bold",force=1,data=subset(diffmatrix, 
maxp > quantile(diffmatrix$maxp,0.995) | 
avg_logFC > 0.4 |
avg_logFC < -1 |
avg_logFC.1 > 0.25 |
avg_logFC.1 < -0.35),label=row.names(subset(diffmatrix, 
maxp > quantile(diffmatrix$maxp,0.995) | 
avg_logFC > 0.4 |
avg_logFC < -1 |
avg_logFC.1 > 0.25 |
avg_logFC.1 < -0.35)
))+xlab("IS vs WD") + ylab("Other vs Control") +theme(
  # get rid of panel grids
  panel.grid.major = element_blank(),
  #panel.grid.major = element_line(color="darkgrey",size=0.1),
  panel.grid.minor = element_blank(),
  #panel.grid.minor = element_line(color="darkgrey",size=0.05),
  # Change plot and panel background
  plot.background=element_rect(fill = "white"),
  panel.background = element_rect(fill = 'black'),
  # Change legend
  legend.background = element_rect(fill = "white", color = NA),
  legend.key = element_rect(color = "gray", fill = "white"),
  legend.title = element_text(color = "Black"),
  legend.text = element_text(color = "black")
  )
#magma,inferno, plasma,viridis
#scale_colour_gradient(low = "darkgreen", high = "red")
#Do reactome analysis at the bottom of script
i=1
j=1
for(i in 1:length(ReactomeTerms)){
pwydata <- as.data.frame(ReactomeTerms[[i]])
geneset <- strsplit(pwydata$geneID, "/")
FCmeans <- data.frame()
for(j in 1:length(geneset)){
  if(length(geneset)>0){
  geneset2FC <- geneset[[j]]
  geneset2FC[which(geneset2FC %in% c("ND2"))] <- "mt-Nd2"
  geneset2FC[which(geneset2FC %in% c("ND3"))] <- "mt-Nd3"
   geneset2FC[which(geneset2FC %in% c("ND5"))] <- "mt-Nd5"
 geneset2FC <- which(row.names(diffmatrix) %in% geneset2FC)
 FC <- mean(diffmatrix$avg_logFC[geneset2FC],na.rm=T)
 FCvar <- var(diffmatrix$avg_logFC[geneset2FC],na.rm=T)
 FC.1 <- mean(diffmatrix$avg_logFC.1[geneset2FC],na.rm=T)
 FC.1var <- var(diffmatrix$avg_logFC.1[geneset2FC],na.rm=T)
 
FCmeans <- rbind(FCmeans,cbind(FC,FC.1,FCvar,FC.1var))
}
}
ReactomeTerms[[i]] <- cbind(ReactomeTerms[[i]],FCmeans)
}
pathmatrix <- rbind(as.data.frame(ReactomeTerms[[1]]),as.data.frame(ReactomeTerms[[2]]),as.data.frame(ReactomeTerms[[3]]),as.data.frame(ReactomeTerms[[4]]))


pathmatrix$p.adjust_original <- pathmatrix$p.adjust
pathmatrix$p.adjust <- -log10(pathmatrix$p.adjust )
pathmatrix$maxFC <- sum(abs(pathmatrix$FC),abs(pathmatrix$FC.1))
pathmatrix <- subset(pathmatrix, pathmatrix$Count > 1)
pathmatrix$AdjSelect <- pathmatrix$p.adjust*(1000*(0.2+abs(pathmatrix$FC.1)))
pathmatrix$neglogqvalue <- -log10(pathmatrix$qvalue)
pathmatrix2 <- pathmatrix[duplicated(pathmatrix$geneID),]
pathmatrix <- pathmatrix[!duplicated(pathmatrix$geneID),]
#pathmatrix <- rbind(pathmatrix,pathmatrix2[!duplicated(pathmatrix2$geneID),])

ggplot(pathmatrix,aes(FC,y=FC.1,colour=p.adjust_original),label=pathmatrix$Description)+ geom_point(size=pathmatrix$Count,alpha=0.5) +scale_colour_viridis_c(direction = +1,option = "viridis") +
  geom_hline(yintercept= 0,linetype="solid",size=0.5,color="black",alpha=0.5)+
  geom_hline(yintercept= 0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_hline(yintercept= -0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_vline(xintercept= 0,linetype="solid",size=0.5,color="black",alpha=0.5)+
  geom_vline(xintercept= 0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_vline(xintercept= -0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_text_repel(size=2,fontface="bold",force=20,data=
subset(pathmatrix, 
abs(pathmatrix$AdjSelect) > quantile(
abs(pathmatrix$AdjSelect),1,na.rm=T) | abs(pathmatrix$p.adjust) > quantile(
abs(pathmatrix$p.adjust),0.75,na.rm=T) |
  abs(pathmatrix$FC.1) > quantile(abs(pathmatrix$FC.1),1,na.rm=T)),
label=subset(pathmatrix, 
abs(pathmatrix$AdjSelect) > quantile(abs(pathmatrix$AdjSelect),1,na.rm=T) |  
  abs(pathmatrix$p.adjust) > quantile(abs(pathmatrix$p.adjust),0.75,na.rm=T) |
  abs(pathmatrix$FC.1) > quantile(abs(pathmatrix$FC.1),1,na.rm=T))$Description,box.padding = 0.5)+xlab("IS vs WD") + ylab("Other vs Control") 

# +theme(
#   # get rid of panel grids
#   panel.grid.major = element_blank(),
#   panel.grid.minor = element_blank(),
#   # Change plot and panel background
#   plot.background=element_rect(fill = "white"),
#   panel.background = element_rect(fill = 'black'),
#   # Change legend
#   legend.background = element_rect(fill = "white", color = NA),
#   legend.key = element_rect(color = "gray", fill = "white"),
#   legend.title = element_text(color = "Black"),
#   legend.text = element_text(color = "black")
#   )
#scale_colour_gradient(low = "yellow", high = "red") +
#scale_colour_viridis_c(direction = -1)
#scale_colour_gradient(low = "black", high = "red")
diffmatrix <- diffmatrix[row.names(Lower_diff),]
ggplot(diffmatrix,aes(avg_logFC.1,y=avg_logFC,color=avg_logFC,label=row.names(diffmatrix)))+ geom_point(size=1,alpha=1)+scale_colour_gradient2(low = "yellow",mid="black" ,high = "red")+ geom_text_repel(fontface="plain",data=subset(diffmatrix, p_val_adj.1 < 0.01 & abs(avg_logFC.1) > 0.25),label=row.names(subset(diffmatrix, p_val_adj.1 < 0.01 & abs(avg_logFC.1) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value") #+ geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8) 

ggplot(diffmatrix,aes(avg_logFC.1,y=log_p_val.1,color=avg_logFC,label=row.names(diffmatrix)))+ geom_point(size=1)+scale_colour_gradient2(low = "yellow",mid="black" ,high = "red")+ geom_text_repel(fontface="plain",data=subset(diffmatrix, p_val_adj.1 < 0.01 & abs(avg_logFC.1) > 0.25),label=row.names(subset(diffmatrix, p_val_adj.1 < 0.01 & abs(avg_logFC.1) > 0.25)))+xlab("log2_FC") + ylab("-log10_p-value")
FC <- diffmatrix$avg_logFC
names(FC) <- row.names(diffmatrix)
cnetplot(ReactomeTerms[[3]], showCategory = 3,categorySize="pvalue", foldChange=FC)

This will hopefully have given us two sets of differentially expressed genes, that should have minimal effect of the MOL-state effect, and should instead lay bare the DTA effect clearly.

By now you propably have seen recurring genes, very similar to the list of genes we already had before in the paper, below I will try to tease out what might be going wrong/is compensated in the ablated MOL5 cells. And with a bit of luck this reflects a general mechanism in the ablated population.

Here I make a distinction between “Upper” and “Lower”, simply referring to the MOL5 upper two clusters, and lower two clusters of the UMAP respectively.

Here is the genelist of the genes shared between the Upper and Lower clusters, in terms of DTA effect.

Upper_diff <- subset(oligos.integrated.samplediffDTA2vs1RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)
Lower_diff <- subset(oligos.integrated.samplediffDTA3vs5RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)
AlldiffgenesHetMOL5 <- unique(c(row.names(Upper_diff),row.names(Lower_diff)))
subset2 <- oligos.integrated.samplediffDTA2vs1RNA[AlldiffgenesHetMOL5,]
subset3 <- oligos.integrated.samplediffDTA3vs5RNA[AlldiffgenesHetMOL5,]
subsetMOL5 <- cbind(subset2,subset3)
colnames(subsetMOL5) <- make.unique(colnames(subsetMOL5))
diffmatrix <- subsetMOL5
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj)
q95pgenes1 <- row.names(diffmatrix[which(diffmatrix$log_p_val >= quantile(diffmatrix$log_p_val,0)),])
diffmatrix$log_p_val.1 <- -log10(diffmatrix$p_val_adj.1)
q95pgenes2 <- row.names(diffmatrix[which(diffmatrix$log_p_val.1 >= quantile(diffmatrix$log_p_val.1,0)),])
q95pgenes <- unique(c(q95pgenes1,q95pgenes2))
diffmatrix <- diffmatrix[q95pgenes,]

# diffmatrix$avg_logFC <- runif(nrow(diffmatrix),min=0,max=100)
# diffmatrix$avg_logFC <- diffmatrix$avg_logFC-mean(diffmatrix$avg_logFC)
# diffmatrix$avg_logFC.1 <- runif(nrow(diffmatrix),min=0,max=100)
# diffmatrix$avg_logFC.1 <- diffmatrix$avg_logFC.1-mean(diffmatrix$avg_logFC.1)

diffmatrix$logFCsumsubstract <- diffmatrix$avg_logFC-diffmatrix$avg_logFC.1
diffmatrix$logFCsum <- diffmatrix$avg_logFC.1+diffmatrix$avg_logFC
diffmatrix$pvalsum <- diffmatrix$log_p_val-diffmatrix$log_p_val.1
diffmatrix$maxp <- apply(cbind(diffmatrix$log_p_val,diffmatrix$log_p_val.1),1,function(x) max(x)) 
diffmatrix$maxp[is.infinite(diffmatrix$maxp)] <- max(diffmatrix$maxp[!is.infinite(diffmatrix$maxp)])
diffmatrix$maxFC <- apply(cbind(diffmatrix$avg_logFC,diffmatrix$avg_logFC.1),1,function(x) max(x)) 
diffmatrix <- diffmatrix[order(diffmatrix$logFCsum,decreasing = TRUE),]
diffmatrix$order <- seq_len(nrow(diffmatrix))
diffmatrix$Genes <- factor(row.names(diffmatrix),levels=row.names(diffmatrix))
ggplot(diffmatrix,aes(logFCsumsubstract,y=logFCsum,colour=maxp,label=row.names(diffmatrix)))+ geom_point(size=diffmatrix$maxp/75) +scale_colour_gradient(low = "black", high = "red") + geom_text_repel(data=subset(diffmatrix, maxp > quantile(diffmatrix$maxp,0.8) & abs(avg_logFC) > 0),                              label=row.names(subset(diffmatrix, maxp > quantile(diffmatrix$maxp,0.8) & abs(avg_logFC) > 0)))+xlab("log2_FC") + ylab("-log10_p-value") 
#+geom_density2d() #+ geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.8) 
FeaturePlot(oligos.integrated, c("Tmsb4x","Mag","Ppia","Enpp2","Cd81","Apod","Mag","Ywhaq","Qk"),pt.size = 0.1)
FeaturePlot(oligos.integrated, c("Tmsb4x","Tpt1","Fth1","Cnp","Cldn11","Itm2b","Lamp1","Trf"),pt.size = 0.1)
library(ggplot2)
library(scales)
theme_set(theme_classic())

# Plot
ggplot(diffmatrix, aes(x=Genes, y=logFCsum)) + 
  geom_point(col="tomato2", size=abs(diffmatrix$maxFC*10)) +   # Draw points
  geom_segment(aes(x=Genes, 
                   xend=Genes, 
                   y=min(logFCsum), 
                   yend=max(logFCsum)), 
               linetype="dashed", 
               size=0.1) +  # Draw dashed lines
  labs(title="MOL5 Upper Vs Lower")+
  coord_flip()





Alldiff <- rbind(Upper_diff,Lower_diff)
Alldiff$Gene <- row.names(Alldiff)

Shared <- intersect(row.names(Upper_diff),row.names(Lower_diff))
Upper_specific <- setdiff(row.names(Upper_diff),row.names(Lower_diff))
Lower_specific <- setdiff(row.names(Lower_diff),row.names(Upper_diff))
Shared

Lets have a closer look at the proteins expressed by the genes in these gene lists.

For this we will use the STRING database, although this is only compatible with the version 10 database. In the folder I have added the version 11 analysis, which is far more detailed, and I have manually added expression information in the way of colored halos around the genes, which I will come back to later in the analysis.

library("STRINGdb")
 string_db <- STRINGdb$new( version="10", species=10090, score_threshold=400, input_directory="" )
 LowerDTA <- Lower_diff
 LowerDTA$Gene <- row.names(LowerDTA)
 UpperDTA <- Upper_diff
 UpperDTA$Gene <- row.names(UpperDTA)
 DTAOL <- rbind(LowerDTA,UpperDTA)
 DTAOL_mapped <- string_db$map( DTAOL, "Gene", removeUnmappedRows = TRUE )
hits <- DTAOL_mapped$STRING_id
string_db$plot_network( hits )

Now we make a new object and use only the OL found DTA genes to make a tsne and UMAP and cluster them.

Generating the UMAP and TSNE.

If these genes are describing some OL process, maturation or functional it would be interesting to see how well the 151 differentially expressed genes describe OL heterogeneity.

featuresDTA <- unique(c(row.names(subset(oligos.integrated.samplediffDTA2vs1RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25)),row.names(subset(oligos.integrated.samplediffDTA3vs5RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.25))))
oligos.integratedDTA <- RunPCA(oligos.integrated, verbose = FALSE,features=featuresDTA)
ElbowPlot(oligos.integratedDTA)
oligos.integratedDTA <- RunUMAP(oligos.integratedDTA, dims = 1:11)
oligos.integratedDTA <- RunTSNE(oligos.integratedDTA, dims = 1:11)
plots <- DimPlot(oligos.integratedDTA, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
plots <- TSNEPlot(oligos.integratedDTA, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
oligos.integratedDTA <- FindNeighbors(oligos.integratedDTA, dims = 1:11)
oligos.integratedDTA <- FindClusters(oligos.integratedDTA,algorithm = 4,resolution = 0.6)
DimPlot(oligos.integratedDTA, group.by = c("seurat_clusters"), combine = FALSE)
DimPlot(oligos.integratedDTA, group.by = c("predicted.id"), combine = FALSE)
DimPlot(oligos.integratedDTA, group.by = c("Sample"), combine = FALSE)

From the UMAP it seems that these 151 genes do allow us to separate the major OL clusters, and the UMAP seems to place the OPC-COP-NFOL1-NFOL2 progression correctly.

The DTA genes also seem to allow us to cluster the MOLs and even the OPCs.

oligos.integratedDTA.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)

Below follows the heatmap showing the top 10 genes based on fold change for each cluster.

DefaultAssay(oligos.integratedDTA) <- "SCT"
top10 <- oligos.integratedDTA.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
DoHeatmap(oligos.integratedDTA, features = top10$gene) + NoLegend()

And here are the top 2 genes found for each cluster as shown on the UMAP.

DefaultAssay(oligos.integratedDTA) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
top2 <- oligos.integratedDTA.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
FeaturePlot(oligos.integratedDTA, features = top2$gene,pt.size = 0.1)
DefaultAssay(oligos.integratedDTA) <- "SCT"

Here I show expression of some common DTA genes that I know are supposed to be more or less affected, based on the differential expression, and the connectedness in the STRINGdb network.

DefaultAssay(oligos.integratedDTA) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
FeaturePlot(oligos.integratedDTA, c("Itm2b","App", "Mapt","Trf","Ywhaq","Kif1b","Tuba1a", "Dync1h1","Dst","Ank2", "Ank3", "Nfasc","Cntn2","Tppp","Ncam1","Mbp","Car2","Ubb","Prdx1","Fth1","Vdac2","Atp5f1","Sepp1","Hopx","Opalin","Ptgds","Il33"),pt.size = 0.1)
DefaultAssay(oligos.integratedDTA) <- "SCT"

For reference and to check what the expression of healthy OLs should look like for these DTA genes, we make a new object of the Science dataset (without OPC and COP) and use only the DTA genes to make a tsne and UMAP and cluster them, and get the markers. Generating the dataset, UMAP, and TSNE.

oligos.integratedScience <- subset(Science,cell_class %in% c("NFOL1","NFOL2","MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
oligos.integratedScience <- RunPCA(oligos.integratedScience, verbose = FALSE,features=featuresDTA)
ElbowPlot(oligos.integratedScience)
oligos.integratedScience <- RunUMAP(oligos.integratedScience, dims = 1:13,n.neighbors = 20)
#oligos.integratedScience <- RunTSNE(oligos.integratedScience, dims = 1:10)
plots <- DimPlot(oligos.integratedScience, group.by = c("cell_class"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
# plots <- TSNEPlot(oligos.integratedScience, group.by = c("cell_class"), combine = FALSE)
# plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
#     byrow = TRUE, override.aes = list(size = 3))))
# CombinePlots(plots)
oligos.integratedScience <- FindNeighbors(oligos.integratedScience, dims = 1:13)
oligos.integratedScience <- FindClusters(oligos.integratedScience,algorithm = 4,resolution = 0.6)
DimPlot(oligos.integratedScience, group.by = c("seurat_clusters"), combine = FALSE)
DimPlot(oligos.integratedScience, group.by = c("cell_class"), combine = FALSE)
oligos.integratedScience.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)

Below follows the heatmap showing the top 10 genes based on fold change for each cluster.

DefaultAssay(oligos.integratedScience) <- "SCT"
top10 <- oligos.integratedScience.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
DoHeatmap(oligos.integratedScience, features = top10$gene) + NoLegend()

And here are the top 2 genes found for each cluster as shown on the UMAP.

DefaultAssay(oligos.integratedScience) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
top2 <- oligos.integratedScience.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
FeaturePlot(oligos.integratedScience, features = top2$gene,pt.size = 0.1)
DefaultAssay(oligos.integratedScience) <- "SCT"

Here I show expression of some common DTA genes that I know are supposed to be more or less affected, to compared them with the expression of the DTA dataset above.

DefaultAssay(oligos.integratedScience) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
FeaturePlot(oligos.integratedScience, c("Itm2b","Scd2","App", "Mapt","Trf","Ywhaq","Kif1b","Tuba1a", "Dync1h1","Dst","Ank2", "Ank3", "Nfasc","Cntn2","Tppp","Ncam1","Mbp","Car2","Ubb","Prdx1","Fth1","Vdac2","Atp5f1","Sepp1","Hopx","Opalin","Ptgds","Il33","Serpinb1a","Hapln2","Rab37"),pt.size = 1)
DefaultAssay(oligos.integratedScience) <- "SCT"

Now we will start to analyse the DTA genes that we found earlier. Here I chose the reactome database, because it seems to give me actual pathways that might be affected.

library(clusterProfiler)
#Convert to gencode using biomart
library(biomaRt)
DTAOL <- subset(oligos.integrated.samplediffMOL5RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.1)
DTAOL <- subset(oligos.integrated.samplediffDTA23vs15RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0)
DTAOL <- subset(oligos.integrated.samplediffOPCRNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.1)
DTAOL$Gene <- row.names(DTAOL)
DTAOL <- Alldiff
genemodulesGO <- DTAOL[!duplicated(DTAOL$Gene),]
listMarts()
ensembl = useMart("ensembl",dataset="mmusculus_gene_ensembl")
listDatasets(ensembl)
attributes = listAttributes(ensembl)
Biomart_gencode_ensembl84_biotypes <- getBM(attributes=c("mgi_symbol","ensembl_gene_id","entrezgene_id","gene_biotype"), filters = "", values = "", ensembl)
Biomart_gencode_ensembl84_biotypes[, 'gene_biotype'] <- as.factor(Biomart_gencode_ensembl84_biotypes[,'gene_biotype'])
#Filter for only our genes
 Biotype_All_dataset <- subset(Biomart_gencode_ensembl84_biotypes, mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezID <-  subset(Biotype_All_dataset, Biotype_All_dataset$mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezmatched <- entrezID[match(genemodulesGO$Gene,entrezID$mgi_symbol),]
entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
library(clusterProfiler)
#Convert to gencode using biomart
library(biomaRt)
#Subset the differential expression genelist from a seurat diff expression result with the parameters you use.
DTAOL <- subset(oligos.integrated.samplediffMOL5RNA, p_val_adj < 0.01 & abs(avg_logFC) > 0.1)
DTAOL$Gene <- row.names(DTAOL)
#remove any duplicates (sanity check for me)
genemodulesGO <- DTAOL[!duplicated(DTAOL$Gene),]

#Convert to entrez
listMarts()
ensembl = useMart("ensembl",dataset="mmusculus_gene_ensembl")
listDatasets(ensembl)
attributes = listAttributes(ensembl)
Biomart_gencode_ensembl84_biotypes <- getBM(attributes=c("mgi_symbol","ensembl_gene_id","entrezgene_id","gene_biotype"), filters = "", values = "", ensembl)
Biomart_gencode_ensembl84_biotypes[, 'gene_biotype'] <- as.factor(Biomart_gencode_ensembl84_biotypes[,'gene_biotype'])
#Filter for only our genes
 Biotype_All_dataset <- subset(Biomart_gencode_ensembl84_biotypes, mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezID <-  subset(Biotype_All_dataset, Biotype_All_dataset$mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezmatched <- entrezID[match(genemodulesGO$Gene,entrezID$mgi_symbol),]
#you might need to remove NAs
##entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
library(ReactomePA)
library(org.Mm.eg.db)
modulesReactomeOPC <- enrichPathway(gene=allLLIDs,organism="mouse",pvalueCutoff=0.01,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
head(as.data.frame(modulesReactome))

Reactome Analysis

dotplot(modulesReactome, showCategory=20)
emapplot(modulesReactome)
FC <- genemodulesGO$avg_logFC
names(FC) <- genemodulesGO$Gene
cnetplot(modulesReactome, showCategory = 20,categorySize="pvalue", foldChange=FC,colorEdge = T,node_label=T,circular = F)
library(clusterProfiler)
#Convert to gencode using biomart
library(biomaRt)
OPC_diff
OPC_diff$Gene <- row.names(OPC_diff)
genemodulesGO <- OPC_diff
listMarts()
ensembl = useMart("ensembl",dataset="mmusculus_gene_ensembl")
listDatasets(ensembl)
attributes = listAttributes(ensembl)
Biomart_gencode_ensembl84_biotypes <- getBM(attributes=c("mgi_symbol","ensembl_gene_id","entrezgene_id","gene_biotype"), filters = "", values = "", ensembl)
Biomart_gencode_ensembl84_biotypes[, 'gene_biotype'] <- as.factor(Biomart_gencode_ensembl84_biotypes[,'gene_biotype'])
#Filter for only our genes
 Biotype_All_dataset <- subset(Biomart_gencode_ensembl84_biotypes, mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezID <-  subset(Biotype_All_dataset, Biotype_All_dataset$mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezmatched <- entrezID[match(genemodulesGO$Gene,entrezID$mgi_symbol),]
entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
library(ReactomePA)
library(org.Mm.eg.db)
modulesReactome <- enrichPathway(gene=allLLIDs,organism="mouse",pvalueCutoff=0.01,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
head(as.data.frame(modulesReactome))
dotplot(modulesReactome, showCategory=8)
emapplot(modulesReactome)
FC <- genemodulesGO$avg_logFC
names(FC) <- genemodulesGO$Gene
cnetplot(modulesReactome, showCategory = 8,categorySize="pvalue", foldChange=FC)

Label transfer with Science dataset.

Now to transfer the labels of the Science dataset, we will integrate all the datasets together and try to predict the cluster membership of the 10X data. (Some code is hidden to keep it streamlined)

Generating the UMAP and TSNE, for the integrated dataset with the Science dataset.

oligos.integrated.full <- RunPCA(oligos.integrated.full, verbose = FALSE)
ElbowPlot(oligos.integrated.full)
oligos.integrated.full <- RunUMAP(oligos.integrated.full, dims = 1:30)
#oligos.integrated <- RunTSNE(oligos.integrated, dims = 1:30)
plots <- DimPlot(oligos.integrated.full, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
# plots <- TSNEPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
# plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3,
#     byrow = TRUE, override.aes = list(size = 3))))
# CombinePlots(plots)

Here I repeat the plotting of expression of some common genes that I know are supposed to be more or less stable clusters within the OLs, just for reference.

DefaultAssay(oligos.integrated.full) <- "RNA"
#Normalize RNA data for visualization purposes
oligos.integrated.full <- NormalizeData(oligos.integrated.full, verbose = FALSE)
FeaturePlot(oligos.integrated.full, c("Pdgfra", "Top2a","Ptprz1","Bmp4","Itpr2", "Egr1", "Klk6", "Hopx", "Ptgds","Il33"),pt.size = 0.1)
DefaultAssay(oligos.integrated.full) <- "integrated"

Here I set the clustering resolution high enough to include the COPs, this means that the MOLs are broken into more clusters than in the science paper.
I show the clusters on the UMAP so you can see their position.

oligos.integrated.full <- FindNeighbors(oligos.integrated.full, dims = 1:30)
oligos.integrated.full <- FindClusters(oligos.integrated.full,algorithm = 4,resolution = 0.6)
DimPlot(oligos.integrated.full, group.by = c("seurat_clusters"), combine = FALSE)

Below you will find a table of the top 2 markers found for each cluster. pct means percentage of expression, where pct.2 refers to all the cells not in the tested cluster.

oligos.integrated.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)

Below follows the heatmap showing the top 10 genes based on fold change for each cluster, using the Seurat found cluster information.

DefaultAssay(oligos.integrated.full) <- "integrated"
top10 <- oligos.integrated.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
DoHeatmap(oligos.integrated.full, features = top10$gene) + NoLegend()

And here are the top 2 genes found for each cluster as shown on the UMAP.

DefaultAssay(oligos.integrated.full) <- "RNA"
# Normalize RNA data for visualization purposes
oligos.integrated.full <- NormalizeData(oligos.integrated.full, verbose = FALSE)
top2 <- oligos.integrated.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
FeaturePlot(oligos.integrated.full, features = top2$gene,pt.size = 0.1)
DefaultAssay(oligos.integrated.full) <- "integrated"

Label transfer

Now we attempt to transfer the cluster labels of the Science dataset onto the 10X dataset.

DefaultAssay(oligos.integrated.full) <- "integrated"
oligos.query <- oligos.list
for (i in 1:(length(oligos.query)-1)) {
  print(paste("Working on dataset ",i," of",length(oligos.list)))
    oligos.anchors <- FindTransferAnchors(reference = oligos.integrated.full, query =oligos.list[[i]], 
    dims = 1:13,reduction = "cca") 
    predictions <- TransferData(anchorset = oligos.anchors, refdata = oligos.integrated.full$cell_class, 
    dims = 1:13,weight.reduction = "cca")
    oligos.query[[i]] <- AddMetaData(oligos.list[[i]], metadata = predictions)
}
predicted.cellclass <- as.character()
for (i in 1:(length(oligos.query)-1)) {
predicted.cellclass <-   c(predicted.cellclass,oligos.query[[i]]$predicted.id)
}

predicted.cellclass <- c(predicted.cellclass,oligos.integrated.full$cell_class[!is.na(oligos.integrated.full$cell_class)])
table(names(predicted.cellclass)==colnames(oligos.integrated.full))
oligos.integrated.full@meta.data$predicted.cellclass <- predicted.cellclass

Here is the end result projected on the UMAP.

DimPlot(oligos.integrated.full, group.by = c("predicted.cellclass"), combine = FALSE)
DiffMatrix <- list()

diffmatrixnames <- c("oligos.integrated.samplediffAllRNA",
                    "oligos.integrated.samplediffMOL5RNA",
                    "oligos.integrated.samplediffOPCRNA",
                    "oligos.integrated.samplediffDTA2vs3RNA")
                     

do.call(head,as.list(as.name(diffmatrixnames[1])))
library(xlsx)
library(stringr)
setwd("~/Documents/SingleCellData/Networkclustering/ElisaAnalysis/IntegrationDTAnetwork/Figures/")
file <- paste("DifferentialExpression.xlsx", sep = "")

for(i in 1:length(diffmatrixnames)){
if(i==1){
write.xlsx(as.data.frame(get(diffmatrixnames[i])), file, sheetName = str_sub(diffmatrixnames[i],start=-10)) }
if(i>1){
write.xlsx(as.data.frame(get(diffmatrixnames[i])), file, sheetName = str_sub(diffmatrixnames[i],start=-10), append = TRUE)
}
}
DiffMatrix <- list()

diffmatrixnames <- c("modulesReactomeMOL5",
                    "modulesReactomeOPC")
                     

do.call(head,as.list(as.name(diffmatrixnames[1])))

diffmatrixnames <- c("modulesReactomeMOL5",
                    "modulesReactomeOPC")

library(xlsx)
library(stringr)
setwd("~/Documents/SingleCellData/Networkclustering/ElisaAnalysis/IntegrationDTAnetwork/Figures/")
file <- paste("Pathwayanalysis.xlsx", sep = "")

for(i in 1:length(diffmatrixnames)){
if(i==1){
write.xlsx(as.data.frame(get(diffmatrixnames[i])), file, sheetName = str_sub(diffmatrixnames[i],start=-10)) }
if(i>1){
write.xlsx(as.data.frame(get(diffmatrixnames[i])), file, sheetName = str_sub(diffmatrixnames[i],start=-10), append = TRUE)
}
}

setwd("~/Documents/SingleCellData/Networkclustering/ElisaAnalysis/IntegrationDTAnetwork/")
write.xlsx(oligos.integrated.markers,file="Enrichedgenespercluster.xlsx", sheetName = "EnrichedClustersWilcoxon")

To make some sense of this I have annotated the STRINGdb network with up/down regulation. Red means down in DTA and green up in DTA.

STRINGdb network of MOL DTA differentially expressed genes

LS0tCnRpdGxlOiAiU2V1cmF0IEludGVncmF0aW9uIFBpcGVsaW5lIGZvciBBYmxhdGlvbiBkYXRhIgphdXRob3I6ICJEYXZpZCB2YW4gQnJ1Z2dlbiIgCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSlgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpJbiB0aGlzIG5vdGVib29rLCBJIGhhdmUgY3JlYXRlZCBhIHN0ZXAtYnktc3RlcCBkb2N1bWVudGF0aW9uIG9mIHRoZSBhbmFseXNpcywgYW5kIEkgd2lsbCB0cnkgdG8gY29tbWVudCBhcyBtdWNoIGFzIHBvc3NpYmxlIHRvIG1ha2UgY2xlYXIgd2hhdCBJIGhhdmUgYW5hbHlzZWQgYW5kIHdoYXQgSSB0aGluayBjb3VsZCBiZSBpbXBvcnRhbnQgcmVzdWx0cyB0aGF0IG1pZ2h0IGluZGljYXRlIHdoYXQgYmlvbG9neSBpcyBiZWhpbmQgdGhlIGV4cHJlc3Npb24gZGlmZmVyZW5jZXMgd2Ugb2JzZXJ2ZS4gCgpXZSBlc3RhYmxpc2hlZCB0aGF0IHNhbXBsZSA0NyB3YXMgdW5kZWZpbmVkIGFuZCBub24tc2Vuc2ljYWwgaW4gb3VyIHByZXZpb3VzIGFuYWx5c2lzIGFuZCBpcyB0aHVzIG9taXR0ZWQgZnJvbSB0aGlzIGFuYWx5c2lzLiBIZXJlIHdlIHRha2UgdGhlIHJlY2VudCBkYXRhICg1MCw1MSw1MikgdG8gYW5hbHlzZSBhbmQgdW5kZXJzdGFuZCBpZiBhYmxhdGlvbiBzaG93cyBhIGlkZW50aWZ5YWJsZSBlZmZlY3QuCmBgYHtyIGVjaG89RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDQwMDAgKiAxMDI0XjIpCiNMb2FkIGRhdGEKbG9hZCgifi9Eb2N1bWVudHMvRmlsZXN0b3NvcnRvdXQvU2luZ2xlQ2VsbERhdGEvTmV0d29ya2NsdXN0ZXJpbmcvRWxpc2FBbmFseXNpcy9BYmxhdGlvbmRhdGEuUmRhdGEiKQojUHV0IGluIFNldXJhdCBvYmplY3QgYW5kIHNwbGl0IGluIHR3byB0byBwZXJmb3JtIHByZXBub3JtYWxpemF0aW9uCm9saWdvcyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoZW1hdF8xMHgsIG1ldGEuZGF0YSA9ICBhbm5vXzEweCxtaW4uY2VsbHMgPSAzLCBtaW4uZmVhdHVyZXMgPSAyMDApCnNhdmUoYW5ub18xMHgsZW1hdF8xMHgsZmlsZT0ifi9Eb2N1bWVudHMvRmlsZXN0b3NvcnRvdXQvU2luZ2xlQ2VsbERhdGEvTmV0d29ya2NsdXN0ZXJpbmcvRWxpc2FBbmFseXNpcy9BYmxhdGlvbmRhdGEuUmRhdGEiKQpgYGAKICAKTm93IHdlIHBlcmZvcm0gUUMsIGxvb2tpbmcgYXQgdGhlIHBlcmNlbnRhZ2Ugb2YgKiptaXRvY2hvbmRyaWFsIFJOQSoqIHZzICoqb3RoZXIgUk5BKiosIHBsdXMgb3RoZXIgbWV0cmljcy4gIAoqIG5GZWF0dXJlX1JOQSA9IG51bWJlciBvZiBnZW5lcyAgCiogbkNvdW50X1JOQSA9IG51bWJlciBvZiBVTUlzIG9yIENvdW50cyAgCiogcGVyY2VudC5tdCA9IHBlcmNlbnQgb2YgZXhwcmVzc2lvbiBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzIHZlcnN1cyB0aGUgcmVzdApgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CiMgVGhlIFtbIG9wZXJhdG9yIGNhbiBhZGQgY29sdW1ucyB0byBvYmplY3QgbWV0YWRhdGEuIFRoaXMgaXMgYSBncmVhdCBwbGFjZSB0byBzdGFzaCBRQyBzdGF0cwpvbGlnb3NbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KG9saWdvcywgcGF0dGVybiA9ICJebXQtIikKIyBWaXN1YWxpemUgUUMgbWV0cmljcyBhcyBhIHZpb2xpbiBwbG90ClZsblBsb3Qob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMSxwdC5zaXplID0gMC4xKQpgYGAKICAKVGhlIHNhbXBsZXMgc2VlbSB0byBkaWZmZXIgUUMtd2lzZSwgYW5kIG1hbnkgc2FtcGxlcyBzaG93IGEgd2lkZSBzcHJlYWQgZm9yIHRoZSBwZXJjZW50IG9mIG1pdG9jaG9uZHJpYWwgZXhwcmVzc2lvbiwgcmVxdWlyaW5nIGEgc3RyaW5nZW50IGN1dC1vZmYuIE5vdyB3ZSBwbG90IHRoZSBRQyBpbmZvcm1hdGlvbiBpbiBhbm90aGVyIHdheSB0byBzZWUgaWYgd2UgY2FuIGVzdGltYXRlIHRoZXNob2xkcyBmb3IgcmVtb3ZpbmcgYmFkIGNlbGxzIGFuZCBwZXJoYXBzIGRvdWJsZXRzLgoKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBGZWF0dXJlU2NhdHRlciBpcyB0eXBpY2FsbHkgdXNlZCB0byB2aXN1YWxpemUgZmVhdHVyZS1mZWF0dXJlIHJlbGF0aW9uc2hpcHMsIGJ1dCBjYW4gYmUgdXNlZAojIGZvciBhbnl0aGluZyBjYWxjdWxhdGVkIGJ5IHRoZSBvYmplY3QsIGkuZS4gY29sdW1ucyBpbiBvYmplY3QgbWV0YWRhdGEsIFBDIHNjb3JlcyBldGMuCnBsb3QxIDwtIEZlYXR1cmVTY2F0dGVyKG9saWdvcywgZ3JvdXAuYnkgPSAiU2FtcGxlIixmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIscHQuc2l6ZSA9IDAuNSkKcGxvdDIgPC0gRmVhdHVyZVNjYXR0ZXIob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIixwdC5zaXplID0gMC41KQpDb21iaW5lUGxvdHMocGxvdHMgPSBsaXN0KHBsb3QxLCBwbG90MikpCmBgYAogIApUaGVzZSBzYW1wbGVzIHNlZW0gdG8gYmUgcGVyZm9ybWluZyBkaWZmZXJlbnRseSwgYW5kIHdlIGhhdmUgdmVyeSBoaWdoIHBlcmNlbnQgb2YgbWl0b2Nob25kcmlhbCBnZW5lcyBlc3BlY2lhbGx5IGluIHRoZSBUQ181MCBzYW1wbGUsIHdoaWNoIGNvbmN1cnJlbnRseSBpcyBhbHNvIGxvd2VyIGluIG51bWJlciBvZiBnZW5lcyBleHByZXNzZWQgYW5kIG51bWJlciBvZiBVTUlzIGRldGVjdGVkLgpOb3cgd2Ugd2lsbCByZW1vdmUgY2VsbHMgZXhwcmVzc2luZyBsZXNzIHRoYXQgMjAwIGdlbmVzICh0byByZW1vdmUgYmFkIGNlbGxzKSwgICAKYW5kIG1vcmUgdGhhbiAzMDAwIGdlbmVzICh0byByZW1vdmUgZG91YmxldHMpLiBBbmQgcmVtb3ZlIGNlbGxzIGV4cHJlc3NpbmcgbW9yZSB0aGF0IDUlIG1pdG9jaG9uZHJpYWwgZ2VuZXMuIEFuZCByZXBsb3QgdGhlIFFDIGRhdGEuCgpKdXN0IHRvIGFsbGV2aWF0ZSBhbnkgY29uY2VybnMsIHRoZSBkb3duc3RyZWFtIGFuYWx5c2lzIGRvZXMgbm90IHNlZW0gdG8gYmUgc2lnbmlmaWNhbnRseSBjaGFuZ2VkIHdpdGggbW9yZSBzdHJpbmdlbnQgb3IgbG9vc2VuZWQgY3V0LW9mZnMsIGluIHRlcm1zIG9mIERUQSBzaWduaWZpY2FudCBnZW5lcy4KYGBge3J9CiNDbGVhbiB1cCB0aGUgZGF0YQpvbGlnb3MgPC0gc3Vic2V0KG9saWdvcywgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMjAwICYgbkZlYXR1cmVfUk5BIDwgMzAwMCAmIHBlcmNlbnQubXQgPCAxNSkKbmNvbChvbGlnb3MpCmBgYApOb3cgd2UgbG9vayBhdCB0aGUgY2xlYW5lZC11cCBkYXRhLgpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CiMgVGhlIFtbIG9wZXJhdG9yIGNhbiBhZGQgY29sdW1ucyB0byBvYmplY3QgbWV0YWRhdGEuIFRoaXMgaXMgYSBncmVhdCBwbGFjZSB0byBzdGFzaCBRQyBzdGF0cwpvbGlnb3NbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KG9saWdvcywgcGF0dGVybiA9ICJebXQtIikKIyBWaXN1YWxpemUgUUMgbWV0cmljcyBhcyBhIHZpb2xpbiBwbG90ClZsblBsb3Qob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMSxwdC5zaXplID0gMC4xKQpgYGAKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBGZWF0dXJlU2NhdHRlciBpcyB0eXBpY2FsbHkgdXNlZCB0byB2aXN1YWxpemUgZmVhdHVyZS1mZWF0dXJlIHJlbGF0aW9uc2hpcHMsIGJ1dCBjYW4gYmUgdXNlZAojIGZvciBhbnl0aGluZyBjYWxjdWxhdGVkIGJ5IHRoZSBvYmplY3QsIGkuZS4gY29sdW1ucyBpbiBvYmplY3QgbWV0YWRhdGEsIFBDIHNjb3JlcyBldGMuCnBsb3QxIDwtIEZlYXR1cmVTY2F0dGVyKG9saWdvcywgZ3JvdXAuYnkgPSAiU2FtcGxlIixmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIscHQuc2l6ZSA9IDAuNSkKcGxvdDIgPC0gRmVhdHVyZVNjYXR0ZXIob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIixwdC5zaXplID0gMC41KQpDb21iaW5lUGxvdHMocGxvdHMgPSBsaXN0KHBsb3QxLCBwbG90MikpCmBgYApOb3cgd2Ugbm9ybWFsaXplIHRoZSBkYXRhc2V0LgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KI29saWdvcy5pbnRlZ3JhdGVkIDwtIFNDVHJhbnNmb3JtKG9saWdvcyx2ZXJib3NlID0gRkFMU0UpCiMgb2xpZ29zLmxpc3QgPC0gU3BsaXRPYmplY3Qob2xpZ29zLCBzcGxpdC5ieSA9ICJTYW1wbGUiKQojIGZvciAoaSBpbiAxOmxlbmd0aChvbGlnb3MubGlzdCkpIHsKIyAgICAgb2xpZ29zLmxpc3RbW2ldXSA8LSBTQ1RyYW5zZm9ybShvbGlnb3MubGlzdFtbaV1dLCB2ZXJib3NlID0gRkFMU0UpCiMgfQojIG9saWdvcy5pbnRlZ3JhdGVkIDwtIG1lcmdlKG9saWdvcy5saXN0W1sxXV0sb2xpZ29zLmxpc3QsbWVyZ2UuZGF0YSA9IFRSVUUpCiN1c2UgY29kZSBiZWxvdyB3aGVuIG5vIGludGVncmF0aW9uIGlzIG5lZWRlZApvbGlnb3MuaW50ZWdyYXRlZDwtIFNDVHJhbnNmb3JtKG9saWdvcywgdmVyYm9zZSA9IEZBTFNFLG5jZWxscz1OVUxMKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KI2ludGVncmF0ZQojIG9saWdvcy5mZWF0dXJlcyA8LSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKG9iamVjdC5saXN0ID0gb2xpZ29zLmxpc3QsIG5mZWF0dXJlcyA9IDYwMDApCiMgb2xpZ29zLmxpc3QgPC0gUHJlcFNDVEludGVncmF0aW9uKG9iamVjdC5saXN0ID0gb2xpZ29zLmxpc3QsIGFuY2hvci5mZWF0dXJlcyA9IG9saWdvcy5mZWF0dXJlcywKIyAgICAgdmVyYm9zZSA9IEZBTFNFKQojIG9saWdvcy5hbmNob3JzIDwtIEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMob2JqZWN0Lmxpc3QgPSBvbGlnb3MubGlzdCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwKIyAgICAgYW5jaG9yLmZlYXR1cmVzID0gb2xpZ29zLmZlYXR1cmVzLCB2ZXJib3NlID0gRkFMU0UpCiMgb2xpZ29zLmludGVncmF0ZWQgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBvbGlnb3MuYW5jaG9ycywgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwKIyAgICAgdmVyYm9zZSA9IEZBTFNFKQpgYGAKR2VuZXJhdGluZyB0aGUgVU1BUCBhbmQgVFNORS4KYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkIDwtIFJ1blBDQShvbGlnb3MuaW50ZWdyYXRlZCwgdmVyYm9zZSA9IEZBTFNFKQpvbGlnb3MuaW50ZWdyYXRlZCA8LSBSdW5VTUFQKG9saWdvcy5pbnRlZ3JhdGVkLCBkaW1zID0gMTozMCkKb2xpZ29zLmludGVncmF0ZWQgPC0gUnVuVFNORShvbGlnb3MuaW50ZWdyYXRlZCwgZGltcyA9IDE6MzApCnBsb3RzIDwtIERpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygiU2FtcGxlIiksIGNvbWJpbmUgPSBGQUxTRSkKcGxvdHMgPC0gbGFwcGx5KFggPSBwbG90cywgRlVOID0gZnVuY3Rpb24oeCkgeCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMywgCiAgICBieXJvdyA9IFRSVUUsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkpCkNvbWJpbmVQbG90cyhwbG90cykKcGxvdHMgPC0gVFNORVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygiU2FtcGxlIiksIGNvbWJpbmUgPSBGQUxTRSkKcGxvdHMgPC0gbGFwcGx5KFggPSBwbG90cywgRlVOID0gZnVuY3Rpb24oeCkgeCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMywgCiAgICBieXJvdyA9IFRSVUUsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkpCkNvbWJpbmVQbG90cyhwbG90cykKYGBgICAKQm90aCB0aGUgVU1BUCBhbmQgdFNORSBzaG93IHRoYXQgdGhlIERUQSBhbmQgdGhlIENvbnRyb2wgYXJlIHNlcGFyYXRlZC4gSSB3aWxsIG5vdyBwZXJmb3JtIGNsdXN0ZXJpbmcgYXMgbm9ybWFsLCB0aGVuIEkgd2lsbCBmb2xsb3cgdGhpcyB1cCBieSBpbnRlZ3JhdGluZyB0aGUgZGF0YSB3aXRoIHRoZSBwcmV2aW91cyBkYXRhIGZyb20gdGhlIFNjaWVuY2UgcGFwZXIgdG8gc2hvdyBob3cgdGhlc2UgY2x1c3RlcnMgcmVsYXRlIHRvIHRoZSBvcmlnaW5hbCBwYXBlcnMgY2x1c3RlcnMuCgpOb3cgd2UgcGxvdCBzaG93IGV4cHJlc3Npb24gb2Ygc29tZSBjb21tb24gZ2VuZXMgZm91bmQgcHJldmlvdXNseSB0byBiZSBzdGFibGUgY2x1c3RlcnMgd2l0aGluIHRoZSBtb3VzZSBPTHMsIGp1c3QgZm9yIHJlZmVyZW5jZS4KYGBge3IgZmlnLndpZHRoPTEwfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQpIDwtICJTQ1QiCiMgTm9ybWFsaXplIFJOQSBkYXRhIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzCiNvbGlnb3MuaW50ZWdyYXRlZCA8LSBOb3JtYWxpemVEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLCB2ZXJib3NlID0gRkFMU0UpCiNGZWF0dXJlUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgYygiVG1zYjR4IiwiVHB0MSIsIkZ0aDEiLCJFbnBwMiIsIkFwcCIpLHB0LnNpemUgPSAwLjEpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBjKCJQZGdmcmEiLCAiUHRwcnoxIiwiQm1wNCIsIkl0cHIyIiwgIkVncjEiLCAiS2xrNiIsICJIb3B4IiwgIlB0Z2RzIiwiSWwzMyIpLHB0LnNpemUgPSAwLjEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKYGBgCkFzIHlvdSBjYW4gc2VlLCBtb3N0IG9mIHRoZSBPTCBjbHVzdGVyIHNlZW1zIHRvIGJlIFB0Z2RzL0lsMzMgcG9zdGl0aXZlIGluc3RlYWQgb2YgSG9weC9LbGs2IG1lYW5pbmcgdGhhdCB3ZSBtaWdodCBoYXZlIG1vc3RseSBNT0w1LzYgb2YgdGhlIG9yaWdpbmFsIGNsdXN0ZXIuCiAgCkkgc2hvdyB0aGUgY2x1c3RlcnMgb24gdGhlIFVNQVAgc28geW91IGNhbiBzZWUgdGhlaXIgcG9zaXRpb24uCmBgYHtyfQpvbGlnb3MuaW50ZWdyYXRlZCA8LSBGaW5kTmVpZ2hib3JzKG9saWdvcy5pbnRlZ3JhdGVkLCBkaW1zID0gMTozMCxubi5tZXRob2Q9InJhbm4iKQpvbGlnb3MuaW50ZWdyYXRlZCA8LSBGaW5kQ2x1c3RlcnMob2xpZ29zLmludGVncmF0ZWQsYWxnb3JpdGhtID0gNCxyZXNvbHV0aW9uID0gMC42KQpgYGAKYGBge3J9CkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygic2V1cmF0X2NsdXN0ZXJzIiksIGNvbWJpbmUgPSBGQUxTRSkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQpgYGAKICAKYGBge3J9IApvbGlnb3MuaW50ZWdyYXRlZCRzZXVyYXRfY2x1c3RlcnNfcm4gPC0gZmFjdG9yKG9saWdvcy5pbnRlZ3JhdGVkJHNldXJhdF9jbHVzdGVycywgbGV2ZWxzPSBjKAogICI0IiwKICAiMTEiLAogICIxMCIsCiAgIjkiLAogICI3IiwKICAiNiIsCiAgIjgiLAogICIyIiwKICAiMSIsCiAgIjUiLAogICIzIikpCgpsaWJyYXJ5KHBseXIpCm9saWdvcy5pbnRlZ3JhdGVkJHNldXJhdF9jbHVzdGVyc19ybiAgPC0gcmV2YWx1ZShhcy5mYWN0b3Iob2xpZ29zLmludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzX3JuKSwgYygiNCI9Ik9QQyIsIjExIj0iT1BDIGN5Y2xpbmciLCIxMCI9IkNPUC9ORk9ML01GT0xhIiwiOSI9Ik1GT0xiIiwiNyI9Ik1PTDEiLCI2Ij0iTU9MMmEiLCI4Ij0iTU9MMmIiLCIyIj0iTU9MNS82YSIsIjEiPSJNT0w1LzZiIiwiNSI9Ik1PTDUvNmMiLCIzIj0iTU9MNS82ZCIpKQoKYGBgCmBgYHtyfQpEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBncm91cC5ieSA9IGMoInNldXJhdF9jbHVzdGVyc19ybiIpLCBjb21iaW5lID0gRkFMU0UsbGFiZWw9VCkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJzZXVyYXRfY2x1c3RlcnMiKSwgY29tYmluZSA9IEZBTFNFKQpgYGAKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyBmaW5kIG1hcmtlcnMgZm9yIGV2ZXJ5IGNsdXN0ZXIgY29tcGFyZWQgdG8gYWxsIHJlbWFpbmluZyBjZWxscywgcmVwb3J0IG9ubHkgdGhlIHBvc2l0aXZlIG9uZXMKSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSA8LSBvbGlnb3MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkc2V1cmF0X2NsdXN0ZXJzX3JuCmxpYnJhcnkoZHBseXIpCm9saWdvcy5pbnRlZ3JhdGVkLm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMob2xpZ29zLmludGVncmF0ZWQsIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUpCklkZW50cyhvbGlnb3MuaW50ZWdyYXRlZCkgPC0gb2xpZ29zLmludGVncmF0ZWRAbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycwpgYGAKYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMiwgd3QgPSBhdmdfbG9nRkMpCmBgYAogIApCZWxvdyBmb2xsb3dzIHRoZSBoZWF0bWFwIHNob3dpbmcgdGhlIHRvcCAxMCBnZW5lcyBiYXNlZCBvbiBmb2xkIGNoYW5nZSBmb3IgZWFjaCBjbHVzdGVyLiAgCmBgYHtyIGZpZy53aWR0aD0xMH0KRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiU0NUIgpJZGVudHMob2xpZ29zLmludGVncmF0ZWQpIDwtIG9saWdvcy5pbnRlZ3JhdGVkQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnNfcm4KdG9wMTAgPC0gb2xpZ29zLmludGVncmF0ZWQubWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG4gPSAxMCwgd3QgPSBhdmdfbG9nRkMpCkRvSGVhdG1hcChvbGlnb3MuaW50ZWdyYXRlZCwgZmVhdHVyZXMgPSB0b3AxMCRnZW5lKSArIE5vTGVnZW5kKCkKSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSA8LSBvbGlnb3MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkc2V1cmF0X2NsdXN0ZXJzCmBgYAoKICAKQW5kIGhlcmUgYXJlIHRoZSB0b3AgMiBnZW5lcyBmb3VuZCBmb3IgZWFjaCBjbHVzdGVyIGFzIHNob3duIG9uIHRoZSBVTUFQLgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKIyBOb3JtYWxpemUgUk5BIGRhdGEgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMKI29saWdvcy5pbnRlZ3JhdGVkIDwtIE5vcm1hbGl6ZURhdGEob2xpZ29zLmludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKdG9wMiA8LSBvbGlnb3MuaW50ZWdyYXRlZC5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDIsIHd0ID0gYXZnX2xvZ0ZDKQpGZWF0dXJlUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZmVhdHVyZXMgPSB0b3AyJGdlbmUscHQuc2l6ZSA9IDAuMSkKRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiU0NUIgpgYGAKSGVyZSBJIHNob3cgYSB0YWJsZSB3aXRoIHRoZSBEVEEgcmVsYXRpbmcgdG8gdGhlIFNldXJhdCBmb3VuZCBjbHVzdGVycy4gVFJVRSBtZWFucyBBYmxhdGVkCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTN9CmNvbmRpdGlvbiA8LSBvbGlnb3MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkU2FtcGxlCmNvbmRpdGlvbiA8LSBzdHJzcGxpdChjb25kaXRpb24sIl8iKQpBYmxhdGVkIDwtIGFzLmxvZ2ljYWwoKQpmb3IgKGkgaW4gMTpsZW5ndGgoY29uZGl0aW9uKSkgewpBYmxhdGVkIDwtICAgYyhBYmxhdGVkLCJHRCI9PWNvbmRpdGlvbltbaV1dWzFdKQp9CmNvbmRpdGlvbiA8LSBBYmxhdGVkCkFibGF0ZWQgPC0gYXMuZGF0YS5mcmFtZShBYmxhdGVkKQpyb3cubmFtZXMoQWJsYXRlZCkgPC0gY29sbmFtZXMob2xpZ29zLmludGVncmF0ZWQpCm9saWdvcy5pbnRlZ3JhdGVkIDwtIEFkZE1ldGFEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLEFibGF0ZWQpCnRhYmxlKG9saWdvcy5pbnRlZ3JhdGVkJHNldXJhdF9jbHVzdGVycyxvbGlnb3MuaW50ZWdyYXRlZCRBYmxhdGVkKQpgYGAKCiMjIyMgTGFiZWwgdHJhbnNmZXIKTm93IHdlIGF0dGVtcHQgdG8gdHJhbnNmZXIgdGhlIGNsdXN0ZXIgbGFiZWxzIG9mIHRoZSBTY2llbmNlIGRhdGFzZXQgb250byB0aGUgMTBYIGRhdGFzZXQuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9CmxvYWQoIn4vRG9jdW1lbnRzL0ZpbGVzdG9zb3J0b3V0L1NpbmdsZUNlbGxEYXRhL1NjaWVuY2VkYXRhc2V0L1NjaWVuY2VtYXRyaWNlc2Fubm8uUmRhdGEiKQphbm5vX3NjaWVuY2UkU2FtcGxlIDwtIHJlcCgiU2NpZW5jZSIsbmNvbChlbWF0X3NjaWVuY2UpKQpTY2llbmNlIDwtIENyZWF0ZVNldXJhdE9iamVjdChlbWF0X3NjaWVuY2UsIG1ldGEuZGF0YSA9ICBhbm5vX3NjaWVuY2UsbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQpTY2llbmNlIDwtIFNDVHJhbnNmb3JtKFNjaWVuY2UsIG1pbl9jZWxscz0zLHZlcmJvc2UgPSBGQUxTRSkKCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKCm9saWdvcy5hbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gU2NpZW5jZSwgcXVlcnkgPW9saWdvcy5pbnRlZ3JhdGVkLCBkaW1zID0gMTozMCxwcm9qZWN0LnF1ZXJ5ID0gVCkgCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBvbGlnb3MuYW5jaG9ycywgcmVmZGF0YSA9IFNjaWVuY2UkY2VsbF9jbGFzcywgZGltcyA9IDE6MzApCm9saWdvcy5pbnRlZ3JhdGVkIDwtIEFkZE1ldGFEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpgYGAKYGBge3J9CkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygic2V1cmF0X2NsdXN0ZXJzIiksIGNvbWJpbmUgPSBGQUxTRSkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuaWQiKSwgY29tYmluZSA9IEZBTFNFKQpEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBncm91cC5ieSA9IGMoIlNhbXBsZSIpLCBjb21iaW5lID0gRkFMU0UpCmBgYApBcyB5b3UgY2FuIHNlZSB0aGUgU2NpZW5jZSBwYXBlciBjbHVzdGVycyBhcmUgc2hvd2luZyB3aGF0IHdlIGNvdWxkIGRldGVybWluZSB3aXRoIHRoZSBtYXJrZXJzIGFzIHdlbGwsIG1vc3QgaWYgbm90IGFsbCBvZiB0aGUgRFRBIGVmZmVjdCBvZiB0aGUgTU9McyBpcyBsb2NhdGVkIGludG8gYSBzaW5nbGUgT0wgcG9wdWxhdGlvbiAtIE1PTDUuIE9QQ3Mgb2Zjb3Vyc2UgYWxzbyBoYXZlIGEgRFRBIGVmZmVjdC4gCmBgYHtyfQp0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0ZWQuaWQsb2xpZ29zLmludGVncmF0ZWQkQWJsYXRlZCkKYGBgCmBgYHtyfQpkYXRhIDwtIGFzLmRhdGEuZnJhbWUodGFibGUob2xpZ29zLmludGVncmF0ZWQkU2FtcGxlLG9saWdvcy5pbnRlZ3JhdGVkJHByZWRpY3RlZC5pZCkpCmNvbG5hbWVzKGRhdGEpIDwtIGMoIkNvbmRpdGlvbiIsIkNsdXN0ZXIiLCJGcmVxIikKbGlicmFyeShwbHlyKQpkYXRhJENsdXN0ZXIgIDwtIGZhY3RvcihkYXRhJENsdXN0ZXIsbGV2ZWxzPWMoIk9QQyIsIkNPUCIsIk5GT0wxIiwiTkZPTDIiLCJNRk9MMiIsIk1PTDEiLCJNT0wyIiwiTU9MMyIsIk1PTDQiLCJNT0w1IiwiTU9MNiIpKQojZGF0YSRDbHVzdGVyICA8LSByZXZhbHVlKGFzLmZhY3RvcihkYXRhJENsdXN0ZXIpLGMoIlBQUiI9IlZMTUMiKSkKIyBTdGFja2VkICsgcGVyY2VudApnZ3Bsb3QoZGF0YSwgYWVzKGZpbGw9Q29uZGl0aW9uLCB5PUZyZXEsIHg9Q2x1c3RlcikpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikKZ2dwbG90KGRhdGEsIGFlcyhmaWxsPUNvbmRpdGlvbiwgeT1GcmVxLCB4PUNsdXN0ZXIpKSArIAogICAgZ2VvbV9iYXIoIHN0YXQ9ImlkZW50aXR5IikgKyBzY2FsZV95X2xvZzEwKCkKCmRhdGEgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsb2xpZ29zLmludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzKSkKY29sbmFtZXMoZGF0YSkgPC0gYygiQ29uZGl0aW9uIiwiQ2x1c3RlciIsIkZyZXEiKQpsaWJyYXJ5KHBseXIpCiNkYXRhJENsdXN0ZXIgIDwtIGZhY3RvcihkYXRhJENsdXN0ZXIsbGV2ZWxzPWMoIk9QQyIsIkNPUCIsIk5GT0wxIiwiTkZPTDIiLCJNRk9MMiIsIk1PTDEiLCJNT0wyIiwiTU9MMyIsIk1PTDQiLCJNT0w1IiwiTU9MNiIpKQojZGF0YSRDbHVzdGVyICA8LSByZXZhbHVlKGFzLmZhY3RvcihkYXRhJENsdXN0ZXIpLGMoIlBQUiI9IlZMTUMiKSkKIyBTdGFja2VkICsgcGVyY2VudApnZ3Bsb3QoZGF0YSwgYWVzKGZpbGw9Q29uZGl0aW9uLCB5PUZyZXEsIHg9Q2x1c3RlcikpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikKZ2dwbG90KGRhdGEsIGFlcyhmaWxsPUNvbmRpdGlvbiwgeT1GcmVxLCB4PUNsdXN0ZXIpKSArIAogICAgZ2VvbV9iYXIoIHN0YXQ9ImlkZW50aXR5IikKCmRhdGEgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsb2xpZ29zLmludGVncmF0ZWQkcHJlZGljdGVkLmlkKSkKY29sbmFtZXMoZGF0YSkgPC0gYygiQ29uZGl0aW9uIiwiQ2x1c3RlciIsIkZyZXEiKQpsaWJyYXJ5KHBseXIpCmRhdGEkQ2x1c3RlciAgPC0gZmFjdG9yKGRhdGEkQ2x1c3RlcixsZXZlbHM9YygiT1BDIiwiQ09QIiwiTkZPTDEiLCJORk9MMiIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IikpCmxpYnJhcnkocmVzaGFwZTIpCmRhdGFjYXN0ZWQgPC0gZGNhc3QoZGF0YSxDbHVzdGVyIH4gQ29uZGl0aW9uKQpjYWxjX2NwbSA8LWZ1bmN0aW9uIChleHByX21hdCkgCnsKICAgIG5vcm1fZmFjdG9yIDwtIGNvbFN1bXMoZXhwcl9tYXQpCiAgICByZXR1cm4odCh0KGV4cHJfbWF0KS9ub3JtX2ZhY3RvcikpICogMTBeNTAKfQpkYXRhY2FzdGVkWywyOjRdIDwtIGNhbGNfY3BtKGRhdGFjYXN0ZWRbLDI6NF0pCmRhdGEgPC0gbWVsdChkYXRhY2FzdGVkKQpjb2xuYW1lcyhkYXRhKSA8LSBjKCJDb25kaXRpb24iLCJDbHVzdGVyIiwiRnJlcSIpCiNkYXRhJENsdXN0ZXIgIDwtIHJldmFsdWUoYXMuZmFjdG9yKGRhdGEkQ2x1c3RlciksYygiUFBSIj0iVkxNQyIpKQojIFN0YWNrZWQgKyBwZXJjZW50CmdncGxvdChkYXRhLCBhZXMoZmlsbD1DbHVzdGVyLCB5PUZyZXEsIHg9Q29uZGl0aW9uKSkgKyAKICAgIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIiwgc3RhdD0iaWRlbnRpdHkiKQpnZ3Bsb3QoZGF0YSwgYWVzKGZpbGw9Q2x1c3RlciwgeT1GcmVxLCB4PUNvbmRpdGlvbikpICsgCiAgICBnZW9tX2Jhciggc3RhdD0iaWRlbnRpdHkiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMH0KbGlicmFyeShoZWF0bWFwMykKbGlicmFyeSh2aXJpZGlzKQpjb21wYXJpc29uIDwtc2NhbGUodChzY2FsZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsb2xpZ29zLmludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzKSkpKQpoZWF0bWFwMyhjb21wYXJpc29uW3Jldihyb3cubmFtZXMoY29tcGFyaXNvbikpLF0sIFJvd3YgPSBOQSAsIENvbHYgPSBOQSAsc2NhbGUgPSAibm9uZSIsc3ltbSA9IEYsIG1ldGhvZCA9ICJ3YXJkLkQyIixjb2w9Y29sb3JSYW1wUGFsZXR0ZShjKCJsaW1lZ3JlZW4iLCJibGFjayIsCiJmaXJlYnJpY2szIikpKDEwMjQpLGJhbGFuY2VDb2xvciA9VCxjZXhSb3cgPSAyLGNleENvbCA9IDIsbWFyZ2lucyA9IGMoMTAsIDEwKSkKaGVhdG1hcDMoY29tcGFyaXNvbltyZXYocm93Lm5hbWVzKGNvbXBhcmlzb24pKSxdLCBSb3d2ID0gTkEgLCBDb2x2ID0gTkEgLHNjYWxlID0gIm5vbmUiLHN5bW0gPSBGLCBtZXRob2QgPSAid2FyZC5EMiIsY29sPXZpcmlkaXMoMTAwMCksYmFsYW5jZUNvbG9yID1ULGNleFJvdyA9IDIsY2V4Q29sID0gMixtYXJnaW5zID0gYygxMCwgMTApKQoKbGlicmFyeShoZWF0bWFwMykKbGlicmFyeSh2aXJpZGlzKQpDbHVzdGVyc1NjaWVuY2UgIDwtIGZhY3RvcihvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0ZWQuaWQsbGV2ZWxzPWMoIk9QQyIsIkNPUCIsIk5GT0wxIiwiTkZPTDIiLCJNRk9MMiIsIk1PTDEiLCJNT0wyIiwiTU9MMyIsIk1PTDQiLCJNT0w1IiwiTU9MNiIpKQpjb21wYXJpc29uIDwtc2NhbGUodChzY2FsZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsQ2x1c3RlcnNTY2llbmNlKSkpKQpoZWF0bWFwMyhjb21wYXJpc29uW3Jldihyb3cubmFtZXMoY29tcGFyaXNvbikpLF0sIFJvd3YgPSBOQSAsIENvbHYgPSBOQSAsc2NhbGUgPSAibm9uZSIsc3ltbSA9IEYsIG1ldGhvZCA9ICJ3YXJkLkQyIixjb2w9Y29sb3JSYW1wUGFsZXR0ZShjKCJsaW1lZ3JlZW4iLCJibGFjayIsCiJmaXJlYnJpY2szIikpKDEwMjQpLGJhbGFuY2VDb2xvciA9VCxjZXhSb3cgPSAyLGNleENvbCA9IDIsbWFyZ2lucyA9IGMoMTAsIDEwKSkKaGVhdG1hcDMoY29tcGFyaXNvbltyZXYocm93Lm5hbWVzKGNvbXBhcmlzb24pKSxdLCBSb3d2ID0gTkEgLCBDb2x2ID0gTkEgLHNjYWxlID0gIm5vbmUiLHN5bW0gPSBGLCBtZXRob2QgPSAid2FyZC5EMiIsY29sPXZpcmlkaXMoMTAwMCksYmFsYW5jZUNvbG9yID1GLGNleFJvdyA9IDIsY2V4Q29sID0gMixtYXJnaW5zID0gYygxMCwgMTApKQoKbGlicmFyeShoZWF0bWFwMykKbGlicmFyeSh2aXJpZGlzKQpDbHVzdGVyc1NjaWVuY2UgIDwtIGZhY3RvcihvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0ZWQuaWQsbGV2ZWxzPWMoIk9QQyIsIkNPUCIsIk5GT0wxIiwiTkZPTDIiLCJNRk9MMiIsIk1PTDEiLCJNT0wyIiwiTU9MMyIsIk1PTDQiLCJNT0w1IiwiTU9MNiIpKQpjb21wYXJpc29uIDwtdChzY2FsZSh0KHNjYWxlKHRhYmxlKENsdXN0ZXJzU2NpZW5jZSxvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUpKSkpKQpoZWF0bWFwMyhjb21wYXJpc29uW3Jldihyb3cubmFtZXMoY29tcGFyaXNvbikpLF0sIFJvd3YgPSBOQSAsIENvbHYgPSBOQSAsc2NhbGUgPSAibm9uZSIsc3ltbSA9IEYsIG1ldGhvZCA9ICJ3YXJkLkQyIixjb2w9Y29sb3JSYW1wUGFsZXR0ZShjKCJsaW1lZ3JlZW4iLCJibGFjayIsCiJmaXJlYnJpY2szIikpKDEwMjQpLGJhbGFuY2VDb2xvciA9VCxjZXhSb3cgPSAyLGNleENvbCA9IDIsbWFyZ2lucyA9IGMoMTAsIDEwKSkKaGVhdG1hcDMoY29tcGFyaXNvbltyZXYocm93Lm5hbWVzKGNvbXBhcmlzb24pKSxdLCBSb3d2ID0gTkEgLCBDb2x2ID0gTkEgLHNjYWxlID0gIm5vbmUiLHN5bW0gPSBGLCBtZXRob2QgPSAid2FyZC5EMiIsY29sPXZpcmlkaXMoMTAwMCksYmFsYW5jZUNvbG9yID1GLGNleFJvdyA9IDIsY2V4Q29sID0gMixtYXJnaW5zID0gYygxMCwgMTApKQoKZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKG9saWdvcy5pbnRlZ3JhdGVkJFNhbXBsZSxvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0ZWQuaWQpKQpjb2xuYW1lcyhkYXRhKSA8LSBjKCJDb25kaXRpb24iLCJDbHVzdGVyIiwiRnJlcSIpCmxpYnJhcnkocGx5cikKZGF0YSRDbHVzdGVyICA8LSBmYWN0b3IoZGF0YSRDbHVzdGVyLGxldmVscz1jKCJPUEMiLCJDT1AiLCJORk9MMSIsIk5GT0wyIiwiTUZPTDIiLCJNT0wxIiwiTU9MMiIsIk1PTDMiLCJNT0w0IiwiTU9MNSIsIk1PTDYiLCJQUFIiKSkKbGlicmFyeShyZXNoYXBlMikKZGF0YWNhc3RlZCA8LSBkY2FzdChkYXRhLENsdXN0ZXIgfiBDb25kaXRpb24pCmNhbGNfY3BtIDwtZnVuY3Rpb24gKGV4cHJfbWF0KSAKewogICAgbm9ybV9mYWN0b3IgPC0gY29sU3VtcyhleHByX21hdCkKICAgIHJldHVybih0KHQoZXhwcl9tYXQpL25vcm1fZmFjdG9yKSkKfQpkYXRhY2FzdGVkWywyOjRdIDwtIGNhbGNfY3BtKGRhdGFjYXN0ZWRbLDI6NF0pKjEwMApyb3cubmFtZXMoZGF0YWNhc3RlZCkgPC0gZGF0YWNhc3RlZFssMV0KZGF0YWNhc3RlZCA8LSBkYXRhY2FzdGVkWywyOjRdCmNvbXBhcmlzb24gPC10KHNjYWxlKHQoZGF0YWNhc3RlZCkpKQpoZWF0bWFwMyhjb21wYXJpc29uW3Jldihyb3cubmFtZXMoY29tcGFyaXNvbikpLF0sIFJvd3YgPSBOQSAsIENvbHYgPSBOQSAsc2NhbGUgPSAibm9uZSIsc3ltbSA9IEYsIG1ldGhvZCA9ICJ3YXJkLkQyIixjb2w9dmlyaWRpcygxMDAwKSxiYWxhbmNlQ29sb3IgPUYsY2V4Um93ID0gMixjZXhDb2wgPSAyLG1hcmdpbnMgPSBjKDEwLCAxMCkpCmNvbXBhcmlzb24gPC1kYXRhY2FzdGVkLWFwcGx5KGRhdGFjYXN0ZWQsMSxmdW5jdGlvbih4KSBtZWFuKHgpKQpoZWF0bWFwMyhjb21wYXJpc29uW3Jldihyb3cubmFtZXMoY29tcGFyaXNvbikpLF0sIFJvd3YgPSBOQSAsIENvbHYgPSBOQSAsc2NhbGUgPSAibm9uZSIsc3ltbSA9IEYsIG1ldGhvZCA9ICJ3YXJkLkQyIixjb2w9dmlyaWRpcygxMDAwKSxiYWxhbmNlQ29sb3IgPUYsY2V4Um93ID0gMixjZXhDb2wgPSAyLG1hcmdpbnMgPSBjKDEwLCAxMCkpCmhlYXRtYXAzKGNvbXBhcmlzb25bcmV2KHJvdy5uYW1lcyhjb21wYXJpc29uKSksXSwgUm93diA9IE5BICwgQ29sdiA9IE5BICxzY2FsZSA9ICJub25lIixzeW1tID0gRiwgbWV0aG9kID0gIndhcmQuRDIiLGNvbD1jb2xvclJhbXBQYWxldHRlKGMoImxpbWVncmVlbiIsImJsYWNrIiwKImZpcmVicmljazMiKSkoMTAyNCksYmFsYW5jZUNvbG9yID1ULGNleFJvdyA9IDIsY2V4Q29sID0gMixtYXJnaW5zID0gYygxMCwgMTApKQpgYGAKYGBge3J9CmRhdGEgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsb2xpZ29zLmludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzX3JuKSkKY29sbmFtZXMoZGF0YSkgPC0gYygiQ29uZGl0aW9uIiwiQ2x1c3RlciIsIkZyZXEiKQpsaWJyYXJ5KHBseXIpCmRhdGEkQ2x1c3RlciAgPC0gZmFjdG9yKGRhdGEkQ2x1c3RlcixsZXZlbHM9YygiT1BDIiwiQ09QIiwiTkZPTDEiLCJORk9MMiIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IiwiUFBSIikpCmxpYnJhcnkocmVzaGFwZTIpCmRhdGFjYXN0ZWQgPC0gZGNhc3QoZGF0YSxDbHVzdGVyIH4gQ29uZGl0aW9uKQpjYWxjX2NwbSA8LWZ1bmN0aW9uIChleHByX21hdCkgCnsKICAgIG5vcm1fZmFjdG9yIDwtIGNvbFN1bXMoZXhwcl9tYXQpCiAgICByZXR1cm4odCh0KGV4cHJfbWF0KS9ub3JtX2ZhY3RvcikpCn0KZGF0YWNhc3RlZFssMjo0XSA8LSBjYWxjX2NwbShkYXRhY2FzdGVkWywyOjRdKSoxMDAKcm93Lm5hbWVzKGRhdGFjYXN0ZWQpIDwtIGRhdGFjYXN0ZWRbLDFdCmRhdGFjYXN0ZWQgPC0gZGF0YWNhc3RlZFssMjo0XQpkYXRhbWVsdGVkIDwtIG1lbHQodChkYXRhY2FzdGVkKSkKZGF0YW1lbHRlZCRWYXIyIDwtIGFzLmZhY3RvcihkYXRhbWVsdGVkJFZhcjIpCmdncGxvdChkYXRhbWVsdGVkLCBhZXMoeSA9IHZhbHVlLCB4ID0gVmFyMikpICsgIyBNb3ZlIHkgYW5kIHggaGVyZSBzbyB0aGFuIHRoZXkgY2FuIGJlIHVzZWQgaW4gc3RhdF8qCiAgICBnZW9tX2RvdHBsb3QoYWVzKGZpbGwgPSBWYXIxKSwgICAjIFVzZSBmaWxsID0gU3BlY2llcyBoZXJlIG5vdCBpbiBnZ3Bsb3QoKQogICAgICAgICAgICAgICAgIGJpbmF4aXMgPSAieSIsICAgICAgICAgIyB3aGljaCBheGlzIHRvIGJpbiBhbG9uZwogICAgICAgICAgICAgICAgIGJpbndpZHRoID0gMS4yNSwgICAgICAgICMgTWluaW1hbCBkaWZmZXJlbmNlIGNvbnNpZGVyZWQgZGlmZmVlcmVudAogICAgICAgICAgICAgICAgIHN0YWNrZGlyID0gImNlbnRlciIsCiAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoMC4yKSMgQ2VudGVyZWQKICAgICAgICAgICAgICAgICApICsgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjNzBCRjQ1IiwiIzU2NzVENiIsIiNDOTUwMkIiKSkrIyBzY2FsZV95X2xvZzEwKCkgKyAKICAgIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGZ1bi55bWluID0gbWVhbiwgZnVuLnltYXggPSBtZWFuLAogICAgICAgICAgICAgICAgIGdlb20gPSAiY3Jvc3NiYXIiLCB3aWR0aCA9IDAuNSxmYXR0ZW4gPSAwLjAxKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQpgYGAKCgpUbyBlc3RhYmxpc2ggd2hhdCBnZW5lcyBhcmUgc2hpZnRlZC91cHJlZ3VsYXRlZC9kb3ducmVndWxhdGVkLCBJIHBlcmZvcm0gYSB0b3VyIGRlIGZvcmNlIChqdXN0IHByZXNzaW5nIGEgYnV0dG9uKSB0byBjYWxjdWxhdGUgZm9yIGVhY2ggaW5kaXZpZHVhbCBwb3B1bGF0aW9uIChub3QgaW5jbHVkaW5nIGNsdXN0ZXJzIHdpdGggdmVyeSBmZXcgYWJsYXRlZCBjZWxscykgCgpPUEMgQ09QIE1GT0wyIE1PTDEgTU9MMiBNT0wzIE1PTDQgTU9MNSBNT0w2CgpJIGVtcGxveSBhZGp1c3RlZCBwLXZhbHVlcywgc28gbG93IG51bWJlcnMgb2YgY2VsbHMgd2lsbCBub3Qgc2hvdyBzaWduaWZpY2FuY2UsIGVzcGVjaWFsbHkgYXMgbG93IGVmZmVjdCBzaXplcyBpbiB0aG9zZSBwb3B1bGF0aW9ucy4KClRoZSBkYXNoZWQgbGluZSBpbmRpY2F0ZXMgdGhlIHRocmVzaG9sZCBmb3IgcDwwLjAxCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9CmxpYnJhcnkoZ2dyZXBlbCkKU2V0SWRlbnQob2xpZ29zLmludGVncmF0ZWQsdmFsdWU9b2xpZ29zLmludGVncmF0ZWRAbWV0YS5kYXRhJHByZWRpY3RlZC5pZCkKSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSA8LSBvbGlnb3MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkcHJlZGljdGVkLmlkCm9saWdvcy5pbnRlZ3JhdGVkJGNsdXN0ZXIgPC0gb2xpZ29zLmludGVncmF0ZWQkcHJlZGljdGVkLmlkCm9saWdvcy5pbnRlZ3JhdGVkJGNlbGx0eXBlLnNhbXBsZSA8LSBwYXN0ZShJZGVudHMob2xpZ29zLmludGVncmF0ZWQpLCBvbGlnb3MuaW50ZWdyYXRlZCRBYmxhdGVkLCBzZXAgPSAiXyIpCm9saWdvcy5pbnRlZ3JhdGVkJGNlbGx0eXBlIDwtIElkZW50cyhvbGlnb3MuaW50ZWdyYXRlZCkKSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiY2VsbHR5cGUuc2FtcGxlIgp0YWJsZShJZGVudHMob2xpZ29zLmludGVncmF0ZWQpKQoKRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiU0NUIgoKb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk9QQ1JOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICJPUENfVFJVRSIsIGlkZW50LjIgPSAiT1BDX0ZBTFNFIiwgdmVyYm9zZSA9IEZBTFNFLGxvZ2ZjLnRocmVzaG9sZCA9IDAuMSxtaW4ucGN0PTApCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZPUENSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmT1BDUk5BCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKzFlLTMwMCkKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KHNpemU9MC4yKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDFlLTMwICYgYWJzKGF2Z19sb2dGQykgPiAwKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDFlLTMwICYgYWJzKGF2Z19sb2dGQykgPiAwKSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlX2FkaiIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjAxKSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuNSkgCgpvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQ09QUk5BIDwtIEZpbmRNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkLCBpZGVudC4xID0gIkNPUF9UUlVFIiwgaWRlbnQuMiA9ICJDT1BfRkFMU0UiLCB2ZXJib3NlID0gRkFMU0UpCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZDT1BSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQ09QUk5BCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9ncF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMyksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cm93Lm5hbWVzKHN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjMpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KSAKCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNRk9MMlJOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICJNRk9MMl9UUlVFIiwgaWRlbnQuMiA9ICJNRk9MMl9GQUxTRSIsIHZlcmJvc2UgPSBGQUxTRSkKI2hlYWQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1GT0wyUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1GT0wyUk5BCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9ncF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMyksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cm93Lm5hbWVzKHN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjMpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KSAKCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNT0wxUk5BIDwtIEZpbmRNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkLCBpZGVudC4xID0gIk1PTDFfVFJVRSIsIGlkZW50LjIgPSAiTU9MMV9GQUxTRSIsIHZlcmJvc2UgPSBGQUxTRSkKI2hlYWQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDFSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MMVJOQQpkaWZmbWF0cml4JGxvZ3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjMpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4zKSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlX2FkaiIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjAxKSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuNSkKCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNT0wyUk5BIDwtIEZpbmRNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkLCBpZGVudC4xID0gIk1PTDJfVFJVRSIsIGlkZW50LjIgPSAiTU9MMl9GQUxTRSIsIHZlcmJvc2UgPSBGQUxTRSkKI2hlYWQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDJSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MMlJOQQpkaWZmbWF0cml4JGxvZ3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjMpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4zKSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlX2FkaiIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjAxKSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuNSkgCgpvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MM1JOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICJNT0wzX1RSVUUiLCBpZGVudC4yID0gIk1PTDNfRkFMU0UiLCB2ZXJib3NlID0gRkFMU0UpCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNT0wzUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDNSTkEKZGlmZm1hdHJpeCRsb2dwX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dwX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludCgpKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4zKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMykpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZV9hZGoiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpIAoKb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDRSTkEgPC0gRmluZE1hcmtlcnMob2xpZ29zLmludGVncmF0ZWQsIGlkZW50LjEgPSAiTU9MNF9UUlVFIiwgaWRlbnQuMiA9ICJNT0w0X0ZBTFNFIiwgdmVyYm9zZSA9IEZBTFNFKQojaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MNFJOQSwgbiA9IDUwKQpkaWZmbWF0cml4IDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNT0w0Uk5BCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9ncF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMyksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cm93Lm5hbWVzKHN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjMpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KQoKb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDVSTkEgPC0gRmluZE1hcmtlcnMob2xpZ29zLmludGVncmF0ZWQsIGlkZW50LjEgPSBjKCJNT0w1X1RSVUUiLCJNT0w2X1RSVUUiKSwgaWRlbnQuMiA9IGMoIk1PTDVfRkFMU0UiLCJNT0w2X0ZBTFNFIiksIHZlcmJvc2UgPSBGQUxTRSxsb2dmYy50aHJlc2hvbGQgPSAwLjEsbWluLnBjdD0wKQojaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MNVJOQSwgbiA9IDUwKQpkaWZmbWF0cml4IDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNT0w1Uk5BCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9ncF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4yNSkpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZV9hZGoiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpCgpvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MNlJOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICJNT0w2X1RSVUUiLCBpZGVudC4yID0gIk1PTDZfRkFMU0UiLCB2ZXJib3NlID0gRkFMU0UpCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZNT0w2Uk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDZSTkEKZGlmZm1hdHJpeCRsb2dwX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dwX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludCgpKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4zKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMykpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZV9hZGoiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpCgpJZGVudHMob2xpZ29zLmludGVncmF0ZWQpIDwtICJBYmxhdGVkIgpvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQWxsUk5BIDwtIEZpbmRNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkLCBpZGVudC4xID0gIlRSVUUiLCBpZGVudC4yID0gIkZBTFNFIiwgdmVyYm9zZSA9IEZBTFNFKQojaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQWxsUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkFsbFJOQQpkaWZmbWF0cml4JGxvZ3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjM1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMzUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KSAKYGBgCmBgYHtyfQojdmVubmRpYWdyYW0KbGlicmFyeShWZW5uRGlhZ3JhbSkKYGBgCgoKQXMgeW91IGNhbiBzZWUgZnJvbSB0aGUgcGxvdHMsIE1PTDUgdGFrZXMgdGhlIGxpb25zIHNoYXJlIG9mIHRoZSBEVEEgZWZmZWN0LCB3aGljaCBzZWVtcyB0byBiZSBjb25jZW50cmF0ZWQgaW4gdGhlIE1PTDUgYW5kIE1PTDMgY2x1c3RlcnMuIE1PTDMgaXMgaW50ZXJlc3RpbmdseSBwcmVkaWN0ZWQgaW4gY2VsbHMgdmVyeSBjbG9zZSB0byB0aGUgRFRBIGNsdXN0ZXJzIGluIHRoZSBVTUFQLgoKVGhlIG90aGVyIG1ham9yIHBhcnQgb2YgdGhlIERUQSBlZmZlY3QgaXMgbG9jYXRlZCBpbiB0aGUgT1BDIGNsdXN0ZXIuCgpGaW5hbGx5LCB0aGUgbGFzdCBwbG90IGlzIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0IG9mIGFsbCB0aGUgY2VsbHMsIGFuZCBhcyB5b3UgY2FuIHNlZSBNT0xzIHNoYXJlIG1hbnkgZ2VuZXMgaW4gdGhlIGVmZmVjdCwgYnV0IE9QQyBzaG93cyBzbGlnaHRseSBkaWZmZXJlbnQgZ2VuZXMuCgpUbyBpbGx1c3RyYXRlIHRoZXNlIGVmZmVjdHMsIEkgd2lsbCBjYWxjdWxhdGUgdGhlIHRvcCAyMCBnZW5lcyBvZiB0aGUgRFRBIGVmZmVjdCBhY3Jvc3MgYWxsIGNlbGxzIGFuZCBzZWUgaG93IHRoZSBtYWpvciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgdHJhbnNsYXRlIHRvIHRoZSBjbHVzdGVycy9VTUFQIHBvc2l0aW9uLgoKYGBge3J9CnRvcDUgPC0gcm93Lm5hbWVzKGhlYWQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkFsbFJOQSwyMCkpCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9Nn0KCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKIyBOb3JtYWxpemUgUk5BIGRhdGEgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMKI29saWdvcy5pbnRlZ3JhdGVkIDwtIE5vcm1hbGl6ZURhdGEob2xpZ29zLmludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKdG9wNSA8LSByb3cubmFtZXMoaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQWxsUk5BLDIwKSkKRmVhdHVyZVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGZlYXR1cmVzID0gdG9wNSxwdC5zaXplID0gMC4xLG5jb2wgPSAyKQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQpIDwtICJTQ1QiCmBgYApBbmQgaGVyZSBhcmUgdmlvbGlucGxvdHMgb2YgdGhlIHNhbWUgZ2VuZXMgYnV0IG5vdyBvcmdhbmlzZWQgcGVyIGNsdXN0ZXIgYW5kIGFibGF0aW9uIGNvbmRpdGlvbi4gQWJsYXRlZCA9IFRSVUUKCmBgYHtyIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0zfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQpIDwtICJTQ1QiCiMgTm9ybWFsaXplIFJOQSBkYXRhIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzCiNvbGlnb3MuaW50ZWdyYXRlZCA8LSBOb3JtYWxpemVEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLCB2ZXJib3NlID0gRkFMU0UpCgojIG9saWdvc2ludGVncmF0ZWQubGlzdCA8LSBTcGxpdE9iamVjdChvbGlnb3MuaW50ZWdyYXRlZCwgc3BsaXQuYnkgPSAiQWJsYXRlZCIpCiMgb2xpZ29zLmludGVncmF0ZWQuY2MgPC0gbWVyZ2Uob2xpZ29zaW50ZWdyYXRlZC5saXN0W1siRkFMU0UiXV0sIHkgPSBvbGlnb3NpbnRlZ3JhdGVkLmxpc3RbWyJUUlVFIl1dLCBtZXJnZS5kYXRhID0gVFJVRSkKcGxvdHMgPC0gVmxuUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZmVhdHVyZXMgPSB0b3A1LCBzcGxpdC5ieSA9ICJBYmxhdGVkIiwgZ3JvdXAuYnkgPSAiY2VsbHR5cGUiLCAKICAgIHB0LnNpemUgPSAwLCBjb21iaW5lID0gRkFMU0UpCkNvbWJpbmVQbG90cyhwbG90cyA9IHBsb3RzLCBuY29sID0gMSkKI3JtKG9saWdvc2ludGVncmF0ZWQubGlzdCkKCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKCmBgYAoKTm93IHRoYXQgdGhpcyBpcyBkb25lLCBpdCdzIGNsZWFyIHRoZSBPTHMgYW5kIE9QQ3MgaGF2ZSBzb21lIGRpZmZlcmVuY2VzIHJlZ2FyZGluZyB0byB0aGUgZ2VuZXMgYWZmZWN0ZWQgYnkgYWJsYXRpb24uIE5vdywgdG8gZ2V0IGFueSBzb3J0IG9mIG1lYW5pbmdmdWxsIGluc2lnaHQgaW50byB0aGUgRFRBIGVmZmVjdCwgaXQgc2VlbXMgbG9naWNhbCB0byBzZXBhcmF0ZSB0aGUgZWZmZWN0IGJ5IGNsdXN0ZXIgdG8gYXZvaWQgTU9MLXN0YXRlIGV4cHJlc3Npb24gdG8gaW5mbHVlbmNlIHRoZSBhbmFseXNpcy4KCklmIHlvdSByZW1lbWJlciBNT0w1IGlzIHRoZSBtb3N0IGFmZmVjdGVkIG9mIHRoZSBNT0xzIG9yIGluIGFueSBjYXNlIHRoZSBtb3N0IHByZXNlbnQgaW4gdGhlIGRhdGFzZXQuIEZ1cnRoZXJtb3JlLCB0aGUgRFRBIGVmZmVjdCBzZWVtZWQgdG8gYmUgY29tcGFyYWJsZSBiZXR3ZWVuIHRoZSBPTCBjbHVzdGVycywgdGhlcmVmb3JlIHRvIGZvY3VzIGNvbXBsZXRlbHkgb24gdGhlIERUQSBlZmZlY3QgSSB3aWxsIG9ubHkgZm9jdXMgb24gTU9MNSwgYmVjYXVzZSBpdCBoYXMgbWFueSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuCgpIZXJlIEkgcGxvdCB0aGUgY2x1c3RlcnMgYWdhaW4gZm9yIHJlZmVyZW5jZS4KYGBge3J9CkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygiU2FtcGxlIiksIGNvbWJpbmUgPSBGQUxTRSkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJzZXVyYXRfY2x1c3RlcnMiKSwgY29tYmluZSA9IEZBTFNFKQpEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBncm91cC5ieSA9IGMoInByZWRpY3RlZC5pZCIpLCBjb21iaW5lID0gRkFMU0UpCmBgYAoKCklmIHlvdSByZW1lbWJlciBmcm9tIHRoZSBTZXVyYXQgY2x1c3RlcmluZywgTU9MNSBpcyBzdWJjbHVzdGVyZWQgaW50byA0IGNsdXN0ZXJzLiBGb3IgdGhlIGFuYWx5c2lzIHRvIHByb2NlZWQgSSB3aWxsIG1ha2UgdGhlIGFzc3VtcHRpb24gdGhhdCB3ZSBoYXZlIGhlYWx0aHkgYW5kIGFmZmVjdGVkIHN0YXRlcyBvZiBNT0w1IGNhcHR1cmVkIGluIHRoZSBTZXVyYXQgY2x1c3RlcnMuCgpGdXJ0aGVybW9yZSwgaXQgc2VlbXMgdGhhdCB3ZSBoYXZlIHR3byBkaXN0aW5jdCBEVEEgc3RhdGVzIHJlYWNoZWQgYW5kIHRoYXQgd2UgY2FuIGRpdmlkZSBNT0w1IGludG8gdHdvIHNpZGVzIChhcyB0aGUgVU1BUCBpcyBzdWdnZXN0aW5nKS4gSGVyZSBJIHBsb3QgMyBnZW5lcyB0aGF0IHNob3cgdGhhdCBNT0w1IGhhcyBoZXRlcm9nZW5vdXMgZXhwcmVzc2lvbiB3aXRoaW4gdGhlIGNsdXN0ZXJzIGJyb2FkbHkgZGl2aWRpbmcgdGhlIGNsdXN0ZXIgaW4gdHdvIChzZWVtaW5nbHkgcmVnYXJkbGVzcyBvZiBEVEEgY29uZGl0aW9uKS4KClNvLCB0aGVyZWZvcmUgSSBtYWtlIGEgc2Vjb25kIGFzc3VtcHRpb24gdGhhdCBTZXVyYXQgY2x1c3RlcnMgMiBhbmQgMSByZXByZXNlbnQgYSBwYWlyIG9mIE1PTDUgc3ViY2x1c3RlcnMgdGhhdCByZXByZXNlbnQgdGhlIGNsb3Nlc3Qgc3RhdGVzIHRvIGVhY2ggb3RoZXIsICoqbW9zdCBsaWtlbHkgcmVwcmVzZW50aW5nIHR3byBzaWRlcyBvZiB0aGUgc2FtZSBjb2luKiosIGFuZCBTZXVyYXQgY2x1c3RlcnMgMyBhbmQgNSByZXByZXNlbnQgYW5vdGhlciBwYWlyIG9mIGNsdXN0ZXJzIGNsb3NlbHkgcmVsYXRlZCB0byBlYWNoIG90aGVyLiAKClRoZXJlZm9yZSBpdCB3b3VsZCBtYWtlIHNlbnNlIHRvIGNvbXBhcmUgU2V1cmF0IGNsdXN0ZXIgMiB2cyAxIGFuZCAzIHZzIDUgYW5kIHRyZWF0IHRoZW0gYXMgMiBkaWZmZW50IGJpb2xvZ2ljYWwgcHJvY2Vzc2VzIHRoYXQgYXJlIGFsdGVyZWQgZm9sbG93aW5nIGFibGF0aW9uLgoKSGVyZSBJIHBsb3QgdGhlIGdlbmVzIHNob3dpbmcgTU9MNSBjYW4gYmUgZGl2aWRlZCBpbnRvIHJvdWdobHkgdHdvIGNsdXN0ZXJzLgoKYGBge3IgZmlnLndpZHRoPTEwfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQpIDwtICJTQ1QiCiMgTm9ybWFsaXplIFJOQSBkYXRhIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzCiNvbGlnb3MuaW50ZWdyYXRlZCA8LSBOb3JtYWxpemVEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLCB2ZXJib3NlID0gRkFMU0UpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBjKCJBcG9kIiwgIlRtc2I0eCIsIlRwcHAzIikscHQuc2l6ZSA9IDEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKYGBgCgoKSGVyZSBJIGludGVyc2VjdCB0aGUgcmVzdWx0cyBvZiBhIGNvdXBsZSBvZiBjbHVzdGVycyB0byBnZXQgc29tZSBzaGFyZWQgYW5kIG5vbi1zaGFyZWQgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIHJlZ2FyZGluZyB0aGUgYWJsYXRpb24uIFNob3dpbmcgYWRqdXN0ZWQgcC12YWx1ZXMuCgpUaGUgdGFrZS1hd2F5IHNob3VsZCBiZSB0aGUgZmlyc3QgdHdvIHBsb3RzLCAgIAoKLSBNT0w1IHNwZWNpZmljIGFibGF0aW9uIGVmZmVjdCwgY29tcGFyZWQgdG8gT1BDCgotIE9QQyBzcGVjaWZpYyBhYmxhdGlvbiBlZmZlY3QsIGNvbXBhcmVkIHRvIE1PTDUKClRoZW4gd2UgaGF2ZSAKCi0gTU9MNSBzaGFyZWQgd2l0aCBPUEMKCi0gT1BDIHNoYXJlZCB3aXRoIE1PTDUKCi0gTU9MNSBzaGFyZWQgd2l0aCBNT0wzCgpgYGB7cn0KT1BDX2RpZmYgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZPUENSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpCk1PTDVfZGlmZiA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDVSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpCk1PTDRfZGlmZiA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDRSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpCk1PTDNfZGlmZiA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDNSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpCgpPUENfTU9MNXNoYXJlZCA8LSBpbnRlcnNlY3Qocm93Lm5hbWVzKE9QQ19kaWZmKSxyb3cubmFtZXMoTU9MNV9kaWZmKSkKT1BDX01PTDVub25zaGFyZWQgPC0gc2V0ZGlmZihyb3cubmFtZXMoT1BDX2RpZmYpLHJvdy5uYW1lcyhNT0w1X2RpZmYpKQpPUENfb25seWNvbXBhcmVkdG9NT0w1PC0gcm93Lm5hbWVzKE9QQ19kaWZmKVshcm93Lm5hbWVzKE9QQ19kaWZmKSAlaW4lIHJvdy5uYW1lcyhNT0w1X2RpZmYpXQpNT0w1X29ubHljb21wYXJlZHRvT1BDPC0gcm93Lm5hbWVzKE1PTDVfZGlmZilbIXJvdy5uYW1lcyhNT0w1X2RpZmYpICVpbiUgcm93Lm5hbWVzKE9QQ19kaWZmKV0KTU9MNV80c2hhcmVkIDwtIGludGVyc2VjdChyb3cubmFtZXMoTU9MNF9kaWZmKSxyb3cubmFtZXMoTU9MNV9kaWZmKSkKTU9MNV8zc2hhcmVkPC0gaW50ZXJzZWN0KHJvdy5uYW1lcyhNT0wzX2RpZmYpLHJvdy5uYW1lcyhNT0w1X2RpZmYpKQpNT0w1XzRfM3NoYXJlZCA8LSBpbnRlcnNlY3Qocm93Lm5hbWVzKE1PTDNfZGlmZiksaW50ZXJzZWN0KHJvdy5uYW1lcyhNT0w0X2RpZmYpLHJvdy5uYW1lcyhNT0w1X2RpZmYpKSkKT1BDX01PTDVzaGFyZWQKT1BDX29ubHljb21wYXJlZHRvTU9MNQpNT0w1X29ubHljb21wYXJlZHRvT1BDClZpZXcoYyhNT0w1X29ubHljb21wYXJlZHRvT1BDLE9QQ19NT0w1c2hhcmVkKSkKVmlldyhjKE9QQ19vbmx5Y29tcGFyZWR0b01PTDUsT1BDX01PTDVzaGFyZWQpKQpWaWV3KGMoTU9MNV80c2hhcmVkLE9QQ19NT0w1c2hhcmVkKSkKCmRpZmZtYXRyaXggPC0gTU9MNV9kaWZmW01PTDVfb25seWNvbXBhcmVkdG9PUEMsXQpkaWZmbWF0cml4JGxvZ3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWUiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpIAoKZGlmZm1hdHJpeCA8LSBPUENfZGlmZltPUENfb25seWNvbXBhcmVkdG9NT0w1LF0KZGlmZm1hdHJpeCRsb2dwX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dwX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludCgpKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4yNSksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cm93Lm5hbWVzKHN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KQoKZGlmZm1hdHJpeCA8LSBNT0w1X2RpZmZbT1BDX01PTDVzaGFyZWQsXQpkaWZmbWF0cml4JGxvZ3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWUiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpIAoKZGlmZm1hdHJpeCA8LSBPUENfZGlmZltPUENfTU9MNXNoYXJlZCxdCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9ncF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4yNSkpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZSIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjAxKSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuNSkgCgpkaWZmbWF0cml4IDwtIE1PTDVfZGlmZltNT0w1XzNzaGFyZWQsXQpkaWZmbWF0cml4JGxvZ3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWUiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpIApgYGAKCkkgbWFkZSB0aGUgc2Vjb25kIGFzc3VtcHRpb24gdGhhdCBTZXVyYXQgY2x1c3RlcnMgMiBhbmQgMSByZXByZXNlbnQgYSBwYWlyIG9mIE1PTDUgc3ViY2x1c3RlcnMgdGhhdCByZXByZXNlbnQgdGhlIGNsb3Nlc3Qgc3RhdGVzIHRvIGVhY2ggb3RoZXIsICoqbW9zdCBsaWtlbHkgcmVwcmVzZW50aW5nIHR3byBzaWRlcyBvZiB0aGUgc2FtZSBjb2luKiosIGFuZCBTZXVyYXQgY2x1c3RlcnMgMyBhbmQgNSByZXByZXNlbnQgYW5vdGhlciBwYWlyIG9mIGNsdXN0ZXJzIGNsb3NlbHkgcmVsYXRlZCB0byBlYWNoIG90aGVyLiAKClRoZXJlZm9yZSBpdCB3b3VsZCBtYWtlIHNlbnNlIHRvIGNvbXBhcmUgU2V1cmF0IGNsdXN0ZXIgMiB2cyAxIGFuZCAzIHZzIDUgYW5kIHRyZWF0IHRoZW0gYXMgMiBkaWZmZW50IGJpb2xvZ2ljYWwgcHJvY2Vzc2VzIHRoYXQgYXJlIGFsdGVyZWQgZm9sbG93aW5nIGFibGF0aW9uLiBXaGljaCBJIGRvIGhlcmU6CgpgYGB7cn0KbGlicmFyeShnZ3JlcGVsKQpTZXRJZGVudChvbGlnb3MuaW50ZWdyYXRlZCx2YWx1ZT1vbGlnb3MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkc2V1cmF0X2NsdXN0ZXJzKQpJZGVudHMob2xpZ29zLmludGVncmF0ZWQpIDwtIG9saWdvcy5pbnRlZ3JhdGVkQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMKb2xpZ29zLmludGVncmF0ZWQkY2x1c3RlciA8LSBvbGlnb3MuaW50ZWdyYXRlZCRzZXVyYXRfY2x1c3RlcnMKb2xpZ29zLmludGVncmF0ZWQkY2VsbHR5cGUuc2FtcGxlIDwtIHBhc3RlKElkZW50cyhvbGlnb3MuaW50ZWdyYXRlZCksIG9saWdvcy5pbnRlZ3JhdGVkJEFibGF0ZWQsIHNlcCA9ICJfIikKb2xpZ29zLmludGVncmF0ZWQkY2VsbHR5cGUgPC0gSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKQojSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiY2VsbHR5cGUuc2FtcGxlIgojdGFibGUoSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSkKCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czFSTkEgPC0gRmluZE1hcmtlcnMob2xpZ29zLmludGVncmF0ZWQsIGlkZW50LjEgPSAiMiIsIGlkZW50LjIgPSBjKCIxIiwiNSIpLCB2ZXJib3NlID0gRkFMU0UsbG9nZmMudGhyZXNob2xkID0gMCxtaW4ucGN0PTApCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEydnMxUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czFSTkEKZGlmZm1hdHJpeCRsb2dfcF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9nX3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KCkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBsb2dfcF92YWwgPiAxMDAgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgbG9nX3BfdmFsID4gMTAwICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC44KSAKCiNWaWV3KHN1YnNldChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBMnZzMVJOQSwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4yNSkpCgpvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBM3ZzNVJOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICIzIiwgaWRlbnQuMiA9IGMoIjEiLCI1IiksIHZlcmJvc2UgPSBGQUxTRSxsb2dmYy50aHJlc2hvbGQgPSAwLG1pbi5wY3Q9MCkKI2hlYWQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTN2czVSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBM3ZzNVJOQQpkaWZmbWF0cml4JGxvZ19wX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dfcF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuNDUpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC40NSkpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZSIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjAxKSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuOCkgCgpvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBMnZzM1JOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICIyIiwgaWRlbnQuMiA9ICIzIiwgdmVyYm9zZSA9IEZBTFNFLGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUsbWluLnBjdD0wLjEpCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEzdnM1Uk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czNSTkEKZGlmZm1hdHJpeCRsb2dfcF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKzFlLTMwMCkKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ19wX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludCgpKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMWUtMjAwICYgYWJzKGF2Z19sb2dGQykgPiAwKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDFlLTIwMCAmIGFicyhhdmdfbG9nRkMpID4gMCkpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZSIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjAxKSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuOCkKCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEyM3ZzMTVSTkEgPC0gRmluZE1hcmtlcnMob2xpZ29zLmludGVncmF0ZWQsIGlkZW50LjEgPSBjKCIyIiwiMyIpLCBpZGVudC4yID0gYygiMSIsIjUiKSwgdmVyYm9zZSA9IEZBTFNFLGxvZ2ZjLnRocmVzaG9sZCA9IDAsbWluLnBjdD0wKQojaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBM3ZzNVJOQSwgbiA9IDUwKQpkaWZmbWF0cml4IDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEyM3ZzMTVSTkEKZGlmZm1hdHJpeCRsb2dfcF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKzFlLTMwMCkKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQyx5PWxvZ19wX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPTAuMikrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAxZS0xMDAgJiBhYnMoYXZnX2xvZ0ZDKSA+IDApLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMWUtMTAwICYgYWJzKGF2Z19sb2dGQykgPiAwKSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC44KQoKI1ZpZXcoc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEzdnM1Uk5BLCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSkKRFRBX2RpZmYgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEyM3ZzMTVSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpCkRUQV9kaWZmX1VMIDwtIHN1YnNldChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBMnZzM1JOQSxyb3cubmFtZXMob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czNSTkEpICVpbiUgcm93Lm5hbWVzKERUQV9kaWZmKSkgCiAgICAgICAgICAgICAgICAgICAgICAKZGlmZm1hdHJpeCA8LSBEVEFfZGlmZl9VTApkaWZmbWF0cml4JGxvZ19wX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGorMWUtMzAwKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9nX3BfdmFsLGxhYmVsPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkpKyBnZW9tX3BvaW50KHNpemU9MC4yKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDFlLTMwICYgYWJzKGF2Z19sb2dGQykgPiAwKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDFlLTMwICYgYWJzKGF2Z19sb2dGQykgPiAwKSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC44KQoKSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiQWJsYXRlZCIKb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkFsbFJOQSA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZCwgaWRlbnQuMSA9ICJUUlVFIiwgaWRlbnQuMiA9ICJGQUxTRSIsIHZlcmJvc2UgPSBGQUxTRSkKaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQWxsUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkFsbFJOQQpkaWZmbWF0cml4JGxvZ19wX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dfcF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoKSsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4yNSkpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZV9hZGoiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpIApgYGAKCmBgYHtyfQpEaWZmTWF0cml4IDwtIGxpc3QoKQoKZGlmZm1hdHJpeG5hbWVzIDwtIGMoIm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEydnMzUk5BIiwKICAgICAgICAgICAgICAgICAgICAib2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTIzdnMxNVJOQSIpCiAgICAgICAgICAgICAgICAgICAgIAoKZG8uY2FsbChoZWFkLGFzLmxpc3QoYXMubmFtZShkaWZmbWF0cml4bmFtZXNbMV0pKSkKYGBgCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKI0NvbnZlcnQgdG8gZ2VuY29kZSB1c2luZyBiaW9tYXJ0CmxpYnJhcnkoYmlvbWFSdCkKbGlzdE1hcnRzKCkKZW5zZW1ibCA9IHVzZU1hcnQoImVuc2VtYmwiLGRhdGFzZXQ9Im1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQpsaXN0RGF0YXNldHMoZW5zZW1ibCkKYXR0cmlidXRlcyA9IGxpc3RBdHRyaWJ1dGVzKGVuc2VtYmwpCkJpb21hcnRfZ2VuY29kZV9lbnNlbWJsODRfYmlvdHlwZXMgPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCJtZ2lfc3ltYm9sIiwiZW5zZW1ibF9nZW5lX2lkIiwiZW50cmV6Z2VuZV9pZCIsImdlbmVfYmlvdHlwZSIpLCBmaWx0ZXJzID0gIiIsIHZhbHVlcyA9ICIiLCBlbnNlbWJsKQpCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzWywgJ2dlbmVfYmlvdHlwZSddIDwtIGFzLmZhY3RvcihCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzWywnZ2VuZV9iaW90eXBlJ10pCiNGaWx0ZXIgZm9yIG9ubHkgb3VyIGdlbmVzCiBCaW90eXBlX0FsbF9kYXRhc2V0IDwtIHN1YnNldChCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzLCBtZ2lfc3ltYm9sICVpbiUgb2xpZ29zLmludGVncmF0ZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpCmVudHJleklEIDwtICBzdWJzZXQoQmlvdHlwZV9BbGxfZGF0YXNldCwgQmlvdHlwZV9BbGxfZGF0YXNldCRtZ2lfc3ltYm9sICVpbiUgb2xpZ29zLmludGVncmF0ZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpCmBgYAoKYGBge3J9CmxpYnJhcnkoUmVhY3RvbWVQQSkKbGlicmFyeShvcmcuTW0uZWcuZGIpClJlYWN0b21lVGVybXMgPC0gbGlzdCgpCmk9MQojVVAKcHZhbGFkaiA8LSAwLjAxCmxvZ2ZjIDwtIDAuMgpmb3IoaSBpbiAxOmxlbmd0aChkaWZmbWF0cml4bmFtZXMpKXsKZGlmZm1hdHJpeCA8LSBkby5jYWxsKCJhcy5kYXRhLmZyYW1lIixhcy5saXN0KGFzLm5hbWUoZGlmZm1hdHJpeG5hbWVzW2ldKSkpCmRpZmZtYXRyaXggPC0gc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IHB2YWxhZGogJiBhdmdfbG9nRkMgPiBsb2dmYykKI3NpZ2dlbmVzIDwtIGhlYWQocm93Lm5hbWVzKGRpZmZtYXRyaXgpLDEwMCkKc2lnZ2VuZXMgPC0gcm93Lm5hbWVzKGRpZmZtYXRyaXgpCmVudHJlem1hdGNoZWQgPC0gZW50cmV6SURbZW50cmV6SUQkbWdpX3N5bWJvbCAlaW4lIHNpZ2dlbmVzLF0KI2VudHJleklEIDwtIGVudHJleklEWyEgYXBwbHkoZW50cmV6SURbLGMoMSwzKV0sIDEsZnVuY3Rpb24gKHgpIGFueU5BKHgpKSxdCmFsbExMSURzIDwtIGVudHJlem1hdGNoZWQkZW50cmV6Z2VuZQptb2R1bGVzUmVhY3RvbWUgPC0gZW5yaWNoUGF0aHdheShnZW5lPWFsbExMSURzLG9yZ2FuaXNtPSJtb3VzZSIscHZhbHVlQ3V0b2ZmPTAuMDUscXZhbHVlQ3V0b2ZmID0gMC4zLHBBZGp1c3RNZXRob2QgPSAibm9uZSIsIHJlYWRhYmxlPVQpCiNtb2R1bGVzUmVhY3RvbWUgPC0gZW5yaWNoR08oZ2VuZT1hbGxMTElEcywib3JnLk1tLmVnLmRiIixwdmFsdWVDdXRvZmY9MC4wNSxxdmFsdWVDdXRvZmYgPSAwLjMscEFkanVzdE1ldGhvZCA9ICJub25lIiwgcmVhZGFibGU9VCkKUmVhY3RvbWVUZXJtc1tbaV1dIDwtIG1vZHVsZXNSZWFjdG9tZQpoZWFkKGFzLmRhdGEuZnJhbWUobW9kdWxlc1JlYWN0b21lKSkKcHJpbnQoaSkKfQpSZWFjdG9tZVRlcm1zW3doaWNoKGxhcHBseShSZWFjdG9tZVRlcm1zLGZ1bmN0aW9uKHgpIGlzLm51bGwoeCkpPT1UUlVFKV0gPC0gIk5vX0dlbmVzIgoKI0FkZCBET1dOIApwdmFsYWRqIDwtIDAuMDEKbG9nZmMgPC0gLTAuMjUKb2Zmc2V0IDwtIGxlbmd0aChSZWFjdG9tZVRlcm1zKQpmb3IoaSBpbiAxOmxlbmd0aChkaWZmbWF0cml4bmFtZXMpKXsKICBpPWkrb2Zmc2V0CmRpZmZtYXRyaXggPC0gZG8uY2FsbCgiYXMuZGF0YS5mcmFtZSIsYXMubGlzdChhcy5uYW1lKGRpZmZtYXRyaXhuYW1lc1tpLW9mZnNldF0pKSkKZGlmZm1hdHJpeCA8LSBzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgcHZhbGFkaiAmIGF2Z19sb2dGQyA8IGxvZ2ZjKQojc2lnZ2VuZXMgPC0gaGVhZChyb3cubmFtZXMoZGlmZm1hdHJpeCksMTAwKQpzaWdnZW5lcyA8LSByb3cubmFtZXMoZGlmZm1hdHJpeCkKZW50cmV6bWF0Y2hlZCA8LSBlbnRyZXpJRFtlbnRyZXpJRCRtZ2lfc3ltYm9sICVpbiUgc2lnZ2VuZXMsXQojZW50cmV6SUQgPC0gZW50cmV6SURbISBhcHBseShlbnRyZXpJRFssYygxLDMpXSwgMSxmdW5jdGlvbiAoeCkgYW55TkEoeCkpLF0KYWxsTExJRHMgPC0gZW50cmV6bWF0Y2hlZCRlbnRyZXpnZW5lCm1vZHVsZXNSZWFjdG9tZSA8LSBlbnJpY2hQYXRod2F5KGdlbmU9YWxsTExJRHMsb3JnYW5pc209Im1vdXNlIixwdmFsdWVDdXRvZmY9MC4wNSxxdmFsdWVDdXRvZmYgPSAwLjMscEFkanVzdE1ldGhvZCA9ICJub25lIiwgcmVhZGFibGU9VCkKI21vZHVsZXNSZWFjdG9tZSA8LSBlbnJpY2hHTyhnZW5lPWFsbExMSURzLCJvcmcuTW0uZWcuZGIiLHB2YWx1ZUN1dG9mZj0wLjA1LHF2YWx1ZUN1dG9mZiA9IDAuMyxwQWRqdXN0TWV0aG9kID0gIm5vbmUiLCByZWFkYWJsZT1UKQpSZWFjdG9tZVRlcm1zW1tpXV0gPC0gbW9kdWxlc1JlYWN0b21lCmhlYWQoYXMuZGF0YS5mcmFtZShtb2R1bGVzUmVhY3RvbWUpKQpwcmludChpKQp9ClJlYWN0b21lVGVybXNbd2hpY2gobGFwcGx5KFJlYWN0b21lVGVybXMsZnVuY3Rpb24oeCkgaXMubnVsbCh4KSk9PVRSVUUpXSA8LSAiTm9fR2VuZXMiCmBgYAoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0KVXBwZXJfZGlmZiA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czNSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDApCkxvd2VyX2RpZmYgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEyM3ZzMTVSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDApCkFsbGRpZmZnZW5lc0hldE1PTDUgPC0gaW50ZXJzZWN0KGludGVyc2VjdChyb3cubmFtZXMob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czNSTkEpLHJvdy5uYW1lcyhvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBMjN2czE1Uk5BKSksdW5pcXVlKGMocm93Lm5hbWVzKFVwcGVyX2RpZmYpLHJvdy5uYW1lcyhMb3dlcl9kaWZmKSkpKQpzdWJzZXQyIDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEydnMzUk5BW0FsbGRpZmZnZW5lc0hldE1PTDUsXQpzdWJzZXQzIDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEyM3ZzMTVSTkFbQWxsZGlmZmdlbmVzSGV0TU9MNSxdCnN1YnNldE1PTDUgPC0gY2JpbmQoc3Vic2V0MixzdWJzZXQzKQpjb2xuYW1lcyhzdWJzZXRNT0w1KSA8LSBtYWtlLnVuaXF1ZShjb2xuYW1lcyhzdWJzZXRNT0w1KSkKZGlmZm1hdHJpeCA8LSBzdWJzZXRNT0w1CmRpZmZtYXRyaXgkbG9nX3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaisoMWUtMzAwKSkKcTk1cGdlbmVzMSA8LSByb3cubmFtZXMoZGlmZm1hdHJpeFt3aGljaChkaWZmbWF0cml4JGxvZ19wX3ZhbCA+PSBxdWFudGlsZShkaWZmbWF0cml4JGxvZ19wX3ZhbCwwKSksXSkKZGlmZm1hdHJpeCRsb2dfcF92YWwuMSA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGouMSsoMWUtMzAwKSkKcTk1cGdlbmVzMiA8LSByb3cubmFtZXMoZGlmZm1hdHJpeFt3aGljaChkaWZmbWF0cml4JGxvZ19wX3ZhbC4xID49IHF1YW50aWxlKGRpZmZtYXRyaXgkbG9nX3BfdmFsLjEsMCkpLF0pCnE5NXBnZW5lcyA8LSB1bmlxdWUoYyhxOTVwZ2VuZXMxLHE5NXBnZW5lczIpKQpkaWZmbWF0cml4IDwtIGRpZmZtYXRyaXhbcTk1cGdlbmVzLF0KZGlmZm1hdHJpeCRhdmdfbG9nRkNbaXMuaW5maW5pdGUoZGlmZm1hdHJpeCRhdmdfbG9nRkMpXSA8LSBtYXgoZGlmZm1hdHJpeCRhdmdfbG9nRkNbIWlzLmluZmluaXRlKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDKV0pCmRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjFbaXMuaW5maW5pdGUoZGlmZm1hdHJpeCRhdmdfbG9nRkMuMSldIDwtIG1heChkaWZmbWF0cml4JGF2Z19sb2dGQy4xWyFpcy5pbmZpbml0ZShkaWZmbWF0cml4JGF2Z19sb2dGQy4xKV0pCiNkaWZmbWF0cml4JGF2Z19sb2dGQy4xIDwtIDIqZGlmZm1hdHJpeCRhdmdfbG9nRkMuMQpkaWZmbWF0cml4JGNvbWJwIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaipkaWZmbWF0cml4JHBfdmFsX2Fkai4xKQpkaWZmbWF0cml4JG1heHAgPC0gYXBwbHkoY2JpbmQoZGlmZm1hdHJpeCRsb2dfcF92YWwsZGlmZm1hdHJpeCRsb2dfcF92YWwuMSksMSxmdW5jdGlvbih4KSBtYXgoeCkpCmRpZmZtYXRyaXgkbWlucCA8LSBhcHBseShjYmluZChkaWZmbWF0cml4JHBfdmFsX2FkaixkaWZmbWF0cml4JHBfdmFsX2Fkai4xKSwxLGZ1bmN0aW9uKHgpIG1pbih4KSkKZGlmZm1hdHJpeCRtYXhwW2lzLmluZmluaXRlKGRpZmZtYXRyaXgkbWF4cCldIDwtIG1heChkaWZmbWF0cml4JG1heHBbIWlzLmluZmluaXRlKGRpZmZtYXRyaXgkbWF4cCldKQpkaWZmbWF0cml4JG1heEZDIDwtIGFwcGx5KGNiaW5kKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjEpLDEsZnVuY3Rpb24oeCkgbWF4KGFicyh4KSkpIApkaWZmbWF0cml4JEdlbmVzIDwtIGZhY3Rvcihyb3cubmFtZXMoZGlmZm1hdHJpeCksbGV2ZWxzPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkKCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1hdmdfbG9nRkMuMSxjb2xvdXI9bWF4cCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPWRpZmZtYXRyaXgkbWF4cC8yMDApICsgc2NhbGVfY29sb3VyX3ZpcmlkaXNfYyhkaXJlY3Rpb24gPSArMSxvcHRpb24gPSAidmlyaWRpcyIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAwLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC4xLGNvbG9yPSJ3aGl0ZSIpKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0gMC4yNSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuMSxjb2xvcj0id2hpdGUiLGFscGhhPTAuNSkrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAtMC4yNSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuMSxjb2xvcj0id2hpdGUiLGFscGhhPTAuNSkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAwLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC4xLGNvbG9yPSJ3aGl0ZSIpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gMC4yNSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuMSxjb2xvcj0id2hpdGUiLGFscGhhPTAuNSkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAtMC4yNSxsaW5ldHlwZT0iZGFzaGVkIixzaXplPTAuMSxjb2xvcj0id2hpdGUiLGFscGhhPTAuNSkrCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemU9Mi41LGZvbnRmYWNlID0gImJvbGQiLGZvcmNlPTEsZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgCm1heHAgPiBxdWFudGlsZShkaWZmbWF0cml4JG1heHAsMC45OTUpIHwgCmF2Z19sb2dGQyA+IDAuNCB8CmF2Z19sb2dGQyA8IC0xIHwKYXZnX2xvZ0ZDLjEgPiAwLjI1IHwKYXZnX2xvZ0ZDLjEgPCAtMC4zNSksbGFiZWw9cm93Lm5hbWVzKHN1YnNldChkaWZmbWF0cml4LCAKbWF4cCA+IHF1YW50aWxlKGRpZmZtYXRyaXgkbWF4cCwwLjk5NSkgfCAKYXZnX2xvZ0ZDID4gMC40IHwKYXZnX2xvZ0ZDIDwgLTEgfAphdmdfbG9nRkMuMSA+IDAuMjUgfAphdmdfbG9nRkMuMSA8IC0wLjM1KQopKSt4bGFiKCJJUyB2cyBXRCIpICsgeWxhYigiT3RoZXIgdnMgQ29udHJvbCIpICt0aGVtZSgKICAjIGdldCByaWQgb2YgcGFuZWwgZ3JpZHMKICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICNwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yPSJkYXJrZ3JleSIsc2l6ZT0wLjEpLAogIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3I9ImRhcmtncmV5IixzaXplPTAuMDUpLAogICMgQ2hhbmdlIHBsb3QgYW5kIHBhbmVsIGJhY2tncm91bmQKICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnYmxhY2snKSwKICAjIENoYW5nZSBsZWdlbmQKICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSBOQSksCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmF5IiwgZmlsbCA9ICJ3aGl0ZSIpLAogIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJCbGFjayIpLAogIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIikKICApCiNtYWdtYSxpbmZlcm5vLCBwbGFzbWEsdmlyaWRpcwojc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICJkYXJrZ3JlZW4iLCBoaWdoID0gInJlZCIpCiNEbyByZWFjdG9tZSBhbmFseXNpcyBhdCB0aGUgYm90dG9tIG9mIHNjcmlwdAppPTEKaj0xCmZvcihpIGluIDE6bGVuZ3RoKFJlYWN0b21lVGVybXMpKXsKcHd5ZGF0YSA8LSBhcy5kYXRhLmZyYW1lKFJlYWN0b21lVGVybXNbW2ldXSkKZ2VuZXNldCA8LSBzdHJzcGxpdChwd3lkYXRhJGdlbmVJRCwgIi8iKQpGQ21lYW5zIDwtIGRhdGEuZnJhbWUoKQpmb3IoaiBpbiAxOmxlbmd0aChnZW5lc2V0KSl7CiAgaWYobGVuZ3RoKGdlbmVzZXQpPjApewogIGdlbmVzZXQyRkMgPC0gZ2VuZXNldFtbal1dCiAgZ2VuZXNldDJGQ1t3aGljaChnZW5lc2V0MkZDICVpbiUgYygiTkQyIikpXSA8LSAibXQtTmQyIgogIGdlbmVzZXQyRkNbd2hpY2goZ2VuZXNldDJGQyAlaW4lIGMoIk5EMyIpKV0gPC0gIm10LU5kMyIKICAgZ2VuZXNldDJGQ1t3aGljaChnZW5lc2V0MkZDICVpbiUgYygiTkQ1IikpXSA8LSAibXQtTmQ1IgogZ2VuZXNldDJGQyA8LSB3aGljaChyb3cubmFtZXMoZGlmZm1hdHJpeCkgJWluJSBnZW5lc2V0MkZDKQogRkMgPC0gbWVhbihkaWZmbWF0cml4JGF2Z19sb2dGQ1tnZW5lc2V0MkZDXSxuYS5ybT1UKQogRkN2YXIgPC0gdmFyKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDW2dlbmVzZXQyRkNdLG5hLnJtPVQpCiBGQy4xIDwtIG1lYW4oZGlmZm1hdHJpeCRhdmdfbG9nRkMuMVtnZW5lc2V0MkZDXSxuYS5ybT1UKQogRkMuMXZhciA8LSB2YXIoZGlmZm1hdHJpeCRhdmdfbG9nRkMuMVtnZW5lc2V0MkZDXSxuYS5ybT1UKQogCkZDbWVhbnMgPC0gcmJpbmQoRkNtZWFucyxjYmluZChGQyxGQy4xLEZDdmFyLEZDLjF2YXIpKQp9Cn0KUmVhY3RvbWVUZXJtc1tbaV1dIDwtIGNiaW5kKFJlYWN0b21lVGVybXNbW2ldXSxGQ21lYW5zKQp9CnBhdGhtYXRyaXggPC0gcmJpbmQoYXMuZGF0YS5mcmFtZShSZWFjdG9tZVRlcm1zW1sxXV0pLGFzLmRhdGEuZnJhbWUoUmVhY3RvbWVUZXJtc1tbMl1dKSxhcy5kYXRhLmZyYW1lKFJlYWN0b21lVGVybXNbWzNdXSksYXMuZGF0YS5mcmFtZShSZWFjdG9tZVRlcm1zW1s0XV0pKQoKCnBhdGhtYXRyaXgkcC5hZGp1c3Rfb3JpZ2luYWwgPC0gcGF0aG1hdHJpeCRwLmFkanVzdApwYXRobWF0cml4JHAuYWRqdXN0IDwtIC1sb2cxMChwYXRobWF0cml4JHAuYWRqdXN0ICkKcGF0aG1hdHJpeCRtYXhGQyA8LSBzdW0oYWJzKHBhdGhtYXRyaXgkRkMpLGFicyhwYXRobWF0cml4JEZDLjEpKQpwYXRobWF0cml4IDwtIHN1YnNldChwYXRobWF0cml4LCBwYXRobWF0cml4JENvdW50ID4gMSkKcGF0aG1hdHJpeCRBZGpTZWxlY3QgPC0gcGF0aG1hdHJpeCRwLmFkanVzdCooMTAwMCooMC4yK2FicyhwYXRobWF0cml4JEZDLjEpKSkKcGF0aG1hdHJpeCRuZWdsb2dxdmFsdWUgPC0gLWxvZzEwKHBhdGhtYXRyaXgkcXZhbHVlKQpwYXRobWF0cml4MiA8LSBwYXRobWF0cml4W2R1cGxpY2F0ZWQocGF0aG1hdHJpeCRnZW5lSUQpLF0KcGF0aG1hdHJpeCA8LSBwYXRobWF0cml4WyFkdXBsaWNhdGVkKHBhdGhtYXRyaXgkZ2VuZUlEKSxdCiNwYXRobWF0cml4IDwtIHJiaW5kKHBhdGhtYXRyaXgscGF0aG1hdHJpeDJbIWR1cGxpY2F0ZWQocGF0aG1hdHJpeDIkZ2VuZUlEKSxdKQoKZ2dwbG90KHBhdGhtYXRyaXgsYWVzKEZDLHk9RkMuMSxjb2xvdXI9cC5hZGp1c3Rfb3JpZ2luYWwpLGxhYmVsPXBhdGhtYXRyaXgkRGVzY3JpcHRpb24pKyBnZW9tX3BvaW50KHNpemU9cGF0aG1hdHJpeCRDb3VudCxhbHBoYT0wLjUpICtzY2FsZV9jb2xvdXJfdmlyaWRpc19jKGRpcmVjdGlvbiA9ICsxLG9wdGlvbiA9ICJ2aXJpZGlzIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0gMCxsaW5ldHlwZT0ic29saWQiLHNpemU9MC41LGNvbG9yPSJibGFjayIsYWxwaGE9MC41KSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9IDAuMjUsbGluZXR5cGU9InNvbGlkIixzaXplPTAuMixjb2xvcj0iYmxhY2siLGFscGhhPTAuNSkrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAtMC4yNSxsaW5ldHlwZT0ic29saWQiLHNpemU9MC4yLGNvbG9yPSJibGFjayIsYWxwaGE9MC41KSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9IDAsbGluZXR5cGU9InNvbGlkIixzaXplPTAuNSxjb2xvcj0iYmxhY2siLGFscGhhPTAuNSkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAwLjI1LGxpbmV0eXBlPSJzb2xpZCIsc2l6ZT0wLjIsY29sb3I9ImJsYWNrIixhbHBoYT0wLjUpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gLTAuMjUsbGluZXR5cGU9InNvbGlkIixzaXplPTAuMixjb2xvcj0iYmxhY2siLGFscGhhPTAuNSkrCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemU9Mixmb250ZmFjZT0iYm9sZCIsZm9yY2U9MjAsZGF0YT0Kc3Vic2V0KHBhdGhtYXRyaXgsIAphYnMocGF0aG1hdHJpeCRBZGpTZWxlY3QpID4gcXVhbnRpbGUoCmFicyhwYXRobWF0cml4JEFkalNlbGVjdCksMSxuYS5ybT1UKSB8IGFicyhwYXRobWF0cml4JHAuYWRqdXN0KSA+IHF1YW50aWxlKAphYnMocGF0aG1hdHJpeCRwLmFkanVzdCksMC43NSxuYS5ybT1UKSB8CiAgYWJzKHBhdGhtYXRyaXgkRkMuMSkgPiBxdWFudGlsZShhYnMocGF0aG1hdHJpeCRGQy4xKSwxLG5hLnJtPVQpKSwKbGFiZWw9c3Vic2V0KHBhdGhtYXRyaXgsIAphYnMocGF0aG1hdHJpeCRBZGpTZWxlY3QpID4gcXVhbnRpbGUoYWJzKHBhdGhtYXRyaXgkQWRqU2VsZWN0KSwxLG5hLnJtPVQpIHwgIAogIGFicyhwYXRobWF0cml4JHAuYWRqdXN0KSA+IHF1YW50aWxlKGFicyhwYXRobWF0cml4JHAuYWRqdXN0KSwwLjc1LG5hLnJtPVQpIHwKICBhYnMocGF0aG1hdHJpeCRGQy4xKSA+IHF1YW50aWxlKGFicyhwYXRobWF0cml4JEZDLjEpLDEsbmEucm09VCkpJERlc2NyaXB0aW9uLGJveC5wYWRkaW5nID0gMC41KSt4bGFiKCJJUyB2cyBXRCIpICsgeWxhYigiT3RoZXIgdnMgQ29udHJvbCIpIAoKIyArdGhlbWUoCiMgICAjIGdldCByaWQgb2YgcGFuZWwgZ3JpZHMKIyAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiMgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAojICAgIyBDaGFuZ2UgcGxvdCBhbmQgcGFuZWwgYmFja2dyb3VuZAojICAgcGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiMgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnYmxhY2snKSwKIyAgICMgQ2hhbmdlIGxlZ2VuZAojICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gTkEpLAojICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmF5IiwgZmlsbCA9ICJ3aGl0ZSIpLAojICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIkJsYWNrIiksCiMgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpCiMgICApCiNzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gInllbGxvdyIsIGhpZ2ggPSAicmVkIikgKwojc2NhbGVfY29sb3VyX3ZpcmlkaXNfYyhkaXJlY3Rpb24gPSAtMSkKI3NjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAiYmxhY2siLCBoaWdoID0gInJlZCIpCmBgYAoKCmBgYHtyfQpkaWZmbWF0cml4IDwtIGRpZmZtYXRyaXhbcm93Lm5hbWVzKExvd2VyX2RpZmYpLF0KZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGF2Z19sb2dGQy4xLHk9YXZnX2xvZ0ZDLGNvbG9yPWF2Z19sb2dGQyxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPTEsYWxwaGE9MSkrc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAieWVsbG93IixtaWQ9ImJsYWNrIiAsaGlnaCA9ICJyZWQiKSsgZ2VvbV90ZXh0X3JlcGVsKGZvbnRmYWNlPSJwbGFpbiIsZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqLjEgPCAwLjAxICYgYWJzKGF2Z19sb2dGQy4xKSA+IDAuMjUpLGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqLjEgPCAwLjAxICYgYWJzKGF2Z19sb2dGQy4xKSA+IDAuMjUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWUiKSAjKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC44KSAKCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMuMSx5PWxvZ19wX3ZhbC4xLGNvbG9yPWF2Z19sb2dGQyxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPTEpK3NjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gInllbGxvdyIsbWlkPSJibGFjayIgLGhpZ2ggPSAicmVkIikrIGdlb21fdGV4dF9yZXBlbChmb250ZmFjZT0icGxhaW4iLGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2Fkai4xIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMuMSkgPiAwLjI1KSxsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2Fkai4xIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMuMSkgPiAwLjI1KSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlIikKCmBgYAoKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9M30KRkMgPC0gZGlmZm1hdHJpeCRhdmdfbG9nRkMKbmFtZXMoRkMpIDwtIHJvdy5uYW1lcyhkaWZmbWF0cml4KQpjbmV0cGxvdChSZWFjdG9tZVRlcm1zW1szXV0sIHNob3dDYXRlZ29yeSA9IDMsY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBmb2xkQ2hhbmdlPUZDKQpgYGAKClRoaXMgd2lsbCBob3BlZnVsbHkgaGF2ZSBnaXZlbiB1cyB0d28gc2V0cyBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMsIHRoYXQgc2hvdWxkIGhhdmUgbWluaW1hbCBlZmZlY3Qgb2YgdGhlIE1PTC1zdGF0ZSBlZmZlY3QsIGFuZCBzaG91bGQgaW5zdGVhZCBsYXkgYmFyZSB0aGUgRFRBIGVmZmVjdCBjbGVhcmx5LgoKQnkgbm93IHlvdSBwcm9wYWJseSBoYXZlIHNlZW4gcmVjdXJyaW5nIGdlbmVzLCB2ZXJ5IHNpbWlsYXIgdG8gdGhlIGxpc3Qgb2YgZ2VuZXMgd2UgYWxyZWFkeSBoYWQgYmVmb3JlIGluIHRoZSBwYXBlciwgYmVsb3cgSSB3aWxsIHRyeSB0byB0ZWFzZSBvdXQgd2hhdCBtaWdodCBiZSBnb2luZyB3cm9uZy9pcyBjb21wZW5zYXRlZCBpbiB0aGUgYWJsYXRlZCBNT0w1IGNlbGxzLiBBbmQgd2l0aCBhIGJpdCBvZiBsdWNrIHRoaXMgcmVmbGVjdHMgYSBnZW5lcmFsIG1lY2hhbmlzbSBpbiB0aGUgYWJsYXRlZCBwb3B1bGF0aW9uLgoKSGVyZSBJIG1ha2UgYSBkaXN0aW5jdGlvbiBiZXR3ZWVuICJVcHBlciIgYW5kICJMb3dlciIsIHNpbXBseSByZWZlcnJpbmcgdG8gdGhlIE1PTDUgdXBwZXIgdHdvIGNsdXN0ZXJzLCBhbmQgbG93ZXIgdHdvIGNsdXN0ZXJzIG9mIHRoZSBVTUFQIHJlc3BlY3RpdmVseS4KCkhlcmUgaXMgdGhlIGdlbmVsaXN0IG9mIHRoZSBnZW5lcyBzaGFyZWQgYmV0d2VlbiB0aGUgVXBwZXIgYW5kIExvd2VyIGNsdXN0ZXJzLCBpbiB0ZXJtcyBvZiBEVEEgZWZmZWN0LgpgYGB7cn0KVXBwZXJfZGlmZiA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czFSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpCkxvd2VyX2RpZmYgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEzdnM1Uk5BLCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KQpBbGxkaWZmZ2VuZXNIZXRNT0w1IDwtIHVuaXF1ZShjKHJvdy5uYW1lcyhVcHBlcl9kaWZmKSxyb3cubmFtZXMoTG93ZXJfZGlmZikpKQpzdWJzZXQyIDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEydnMxUk5BW0FsbGRpZmZnZW5lc0hldE1PTDUsXQpzdWJzZXQzIDwtIG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEzdnM1Uk5BW0FsbGRpZmZnZW5lc0hldE1PTDUsXQpzdWJzZXRNT0w1IDwtIGNiaW5kKHN1YnNldDIsc3Vic2V0MykKY29sbmFtZXMoc3Vic2V0TU9MNSkgPC0gbWFrZS51bmlxdWUoY29sbmFtZXMoc3Vic2V0TU9MNSkpCmRpZmZtYXRyaXggPC0gc3Vic2V0TU9MNQpkaWZmbWF0cml4JGxvZ19wX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCnE5NXBnZW5lczEgPC0gcm93Lm5hbWVzKGRpZmZtYXRyaXhbd2hpY2goZGlmZm1hdHJpeCRsb2dfcF92YWwgPj0gcXVhbnRpbGUoZGlmZm1hdHJpeCRsb2dfcF92YWwsMCkpLF0pCmRpZmZtYXRyaXgkbG9nX3BfdmFsLjEgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqLjEpCnE5NXBnZW5lczIgPC0gcm93Lm5hbWVzKGRpZmZtYXRyaXhbd2hpY2goZGlmZm1hdHJpeCRsb2dfcF92YWwuMSA+PSBxdWFudGlsZShkaWZmbWF0cml4JGxvZ19wX3ZhbC4xLDApKSxdKQpxOTVwZ2VuZXMgPC0gdW5pcXVlKGMocTk1cGdlbmVzMSxxOTVwZ2VuZXMyKSkKZGlmZm1hdHJpeCA8LSBkaWZmbWF0cml4W3E5NXBnZW5lcyxdCgojIGRpZmZtYXRyaXgkYXZnX2xvZ0ZDIDwtIHJ1bmlmKG5yb3coZGlmZm1hdHJpeCksbWluPTAsbWF4PTEwMCkKIyBkaWZmbWF0cml4JGF2Z19sb2dGQyA8LSBkaWZmbWF0cml4JGF2Z19sb2dGQy1tZWFuKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDKQojIGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjEgPC0gcnVuaWYobnJvdyhkaWZmbWF0cml4KSxtaW49MCxtYXg9MTAwKQojIGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjEgPC0gZGlmZm1hdHJpeCRhdmdfbG9nRkMuMS1tZWFuKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjEpCgpkaWZmbWF0cml4JGxvZ0ZDc3Vtc3Vic3RyYWN0IDwtIGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLWRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjEKZGlmZm1hdHJpeCRsb2dGQ3N1bSA8LSBkaWZmbWF0cml4JGF2Z19sb2dGQy4xK2RpZmZtYXRyaXgkYXZnX2xvZ0ZDCmRpZmZtYXRyaXgkcHZhbHN1bSA8LSBkaWZmbWF0cml4JGxvZ19wX3ZhbC1kaWZmbWF0cml4JGxvZ19wX3ZhbC4xCmRpZmZtYXRyaXgkbWF4cCA8LSBhcHBseShjYmluZChkaWZmbWF0cml4JGxvZ19wX3ZhbCxkaWZmbWF0cml4JGxvZ19wX3ZhbC4xKSwxLGZ1bmN0aW9uKHgpIG1heCh4KSkgCmRpZmZtYXRyaXgkbWF4cFtpcy5pbmZpbml0ZShkaWZmbWF0cml4JG1heHApXSA8LSBtYXgoZGlmZm1hdHJpeCRtYXhwWyFpcy5pbmZpbml0ZShkaWZmbWF0cml4JG1heHApXSkKZGlmZm1hdHJpeCRtYXhGQyA8LSBhcHBseShjYmluZChkaWZmbWF0cml4JGF2Z19sb2dGQyxkaWZmbWF0cml4JGF2Z19sb2dGQy4xKSwxLGZ1bmN0aW9uKHgpIG1heCh4KSkgCmRpZmZtYXRyaXggPC0gZGlmZm1hdHJpeFtvcmRlcihkaWZmbWF0cml4JGxvZ0ZDc3VtLGRlY3JlYXNpbmcgPSBUUlVFKSxdCmRpZmZtYXRyaXgkb3JkZXIgPC0gc2VxX2xlbihucm93KGRpZmZtYXRyaXgpKQpkaWZmbWF0cml4JEdlbmVzIDwtIGZhY3Rvcihyb3cubmFtZXMoZGlmZm1hdHJpeCksbGV2ZWxzPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkKZ2dwbG90KGRpZmZtYXRyaXgsYWVzKGxvZ0ZDc3Vtc3Vic3RyYWN0LHk9bG9nRkNzdW0sY29sb3VyPW1heHAsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoc2l6ZT1kaWZmbWF0cml4JG1heHAvNzUpICtzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gImJsYWNrIiwgaGlnaCA9ICJyZWQiKSArIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBtYXhwID4gcXVhbnRpbGUoZGlmZm1hdHJpeCRtYXhwLDAuOCkgJiBhYnMoYXZnX2xvZ0ZDKSA+IDApLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXJvdy5uYW1lcyhzdWJzZXQoZGlmZm1hdHJpeCwgbWF4cCA+IHF1YW50aWxlKGRpZmZtYXRyaXgkbWF4cCwwLjgpICYgYWJzKGF2Z19sb2dGQykgPiAwKSkpK3hsYWIoImxvZzJfRkMiKSArIHlsYWIoIi1sb2cxMF9wLXZhbHVlIikgCiMrZ2VvbV9kZW5zaXR5MmQoKSAjKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC44KSAKRmVhdHVyZVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGMoIlRtc2I0eCIsIk1hZyIsIlBwaWEiLCJFbnBwMiIsIkNkODEiLCJBcG9kIiwiTWFnIiwiWXdoYXEiLCJRayIpLHB0LnNpemUgPSAwLjEpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBjKCJUbXNiNHgiLCJUcHQxIiwiRnRoMSIsIkNucCIsIkNsZG4xMSIsIkl0bTJiIiwiTGFtcDEiLCJUcmYiKSxwdC5zaXplID0gMC4xKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQoKIyBQbG90CmdncGxvdChkaWZmbWF0cml4LCBhZXMoeD1HZW5lcywgeT1sb2dGQ3N1bSkpICsgCiAgZ2VvbV9wb2ludChjb2w9InRvbWF0bzIiLCBzaXplPWFicyhkaWZmbWF0cml4JG1heEZDKjEwKSkgKyAgICMgRHJhdyBwb2ludHMKICBnZW9tX3NlZ21lbnQoYWVzKHg9R2VuZXMsIAogICAgICAgICAgICAgICAgICAgeGVuZD1HZW5lcywgCiAgICAgICAgICAgICAgICAgICB5PW1pbihsb2dGQ3N1bSksIAogICAgICAgICAgICAgICAgICAgeWVuZD1tYXgobG9nRkNzdW0pKSwgCiAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiLCAKICAgICAgICAgICAgICAgc2l6ZT0wLjEpICsgICMgRHJhdyBkYXNoZWQgbGluZXMKICBsYWJzKHRpdGxlPSJNT0w1IFVwcGVyIFZzIExvd2VyIikrCiAgY29vcmRfZmxpcCgpCgoKCgoKQWxsZGlmZiA8LSByYmluZChVcHBlcl9kaWZmLExvd2VyX2RpZmYpCkFsbGRpZmYkR2VuZSA8LSByb3cubmFtZXMoQWxsZGlmZikKClNoYXJlZCA8LSBpbnRlcnNlY3Qocm93Lm5hbWVzKFVwcGVyX2RpZmYpLHJvdy5uYW1lcyhMb3dlcl9kaWZmKSkKVXBwZXJfc3BlY2lmaWMgPC0gc2V0ZGlmZihyb3cubmFtZXMoVXBwZXJfZGlmZikscm93Lm5hbWVzKExvd2VyX2RpZmYpKQpMb3dlcl9zcGVjaWZpYyA8LSBzZXRkaWZmKHJvdy5uYW1lcyhMb3dlcl9kaWZmKSxyb3cubmFtZXMoVXBwZXJfZGlmZikpClNoYXJlZAoKYGBgCgpMZXRzIGhhdmUgYSBjbG9zZXIgbG9vayBhdCB0aGUgcHJvdGVpbnMgZXhwcmVzc2VkIGJ5IHRoZSBnZW5lcyBpbiB0aGVzZSBnZW5lIGxpc3RzLgoKRm9yIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIFNUUklORyBkYXRhYmFzZSwgYWx0aG91Z2ggdGhpcyBpcyBvbmx5IGNvbXBhdGlibGUgd2l0aCB0aGUgdmVyc2lvbiAxMCBkYXRhYmFzZS4gSW4gdGhlIGZvbGRlciBJIGhhdmUgYWRkZWQgdGhlIHZlcnNpb24gMTEgYW5hbHlzaXMsIHdoaWNoIGlzIGZhciBtb3JlIGRldGFpbGVkLCBhbmQgSSBoYXZlIG1hbnVhbGx5IGFkZGVkIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gaW4gdGhlIHdheSBvZiBjb2xvcmVkIGhhbG9zIGFyb3VuZCB0aGUgZ2VuZXMsIHdoaWNoIEkgd2lsbCBjb21lIGJhY2sgdG8gbGF0ZXIgaW4gdGhlIGFuYWx5c2lzLgpgYGB7cn0KbGlicmFyeSgiU1RSSU5HZGIiKQogc3RyaW5nX2RiIDwtIFNUUklOR2RiJG5ldyggdmVyc2lvbj0iMTAiLCBzcGVjaWVzPTEwMDkwLCBzY29yZV90aHJlc2hvbGQ9NDAwLCBpbnB1dF9kaXJlY3Rvcnk9IiIgKQogTG93ZXJEVEEgPC0gTG93ZXJfZGlmZgogTG93ZXJEVEEkR2VuZSA8LSByb3cubmFtZXMoTG93ZXJEVEEpCiBVcHBlckRUQSA8LSBVcHBlcl9kaWZmCiBVcHBlckRUQSRHZW5lIDwtIHJvdy5uYW1lcyhVcHBlckRUQSkKIERUQU9MIDwtIHJiaW5kKExvd2VyRFRBLFVwcGVyRFRBKQogRFRBT0xfbWFwcGVkIDwtIHN0cmluZ19kYiRtYXAoIERUQU9MLCAiR2VuZSIsIHJlbW92ZVVubWFwcGVkUm93cyA9IFRSVUUgKQpgYGAKYGBge3IgZmlnLndpZHRoPTEwfQpoaXRzIDwtIERUQU9MX21hcHBlZCRTVFJJTkdfaWQKc3RyaW5nX2RiJHBsb3RfbmV0d29yayggaGl0cyApCmBgYAoKCk5vdyB3ZSBtYWtlIGEgbmV3IG9iamVjdCBhbmQgdXNlIG9ubHkgdGhlIE9MIGZvdW5kIERUQSBnZW5lcyB0byBtYWtlIGEgdHNuZSBhbmQgVU1BUCBhbmQgY2x1c3RlciB0aGVtLgoKR2VuZXJhdGluZyB0aGUgVU1BUCBhbmQgVFNORS4KCklmIHRoZXNlIGdlbmVzIGFyZSBkZXNjcmliaW5nIHNvbWUgT0wgcHJvY2VzcywgbWF0dXJhdGlvbiBvciBmdW5jdGlvbmFsIGl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIHNlZSBob3cgd2VsbCB0aGUgMTUxIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBkZXNjcmliZSBPTCBoZXRlcm9nZW5laXR5LgoKYGBge3J9CmZlYXR1cmVzRFRBIDwtIHVuaXF1ZShjKHJvdy5uYW1lcyhzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTJ2czFSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMjUpKSxyb3cubmFtZXMoc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZEVEEzdnM1Uk5BLCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjI1KSkpKQpvbGlnb3MuaW50ZWdyYXRlZERUQSA8LSBSdW5QQ0Eob2xpZ29zLmludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSxmZWF0dXJlcz1mZWF0dXJlc0RUQSkKRWxib3dQbG90KG9saWdvcy5pbnRlZ3JhdGVkRFRBKQpgYGAKYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkRFRBIDwtIFJ1blVNQVAob2xpZ29zLmludGVncmF0ZWREVEEsIGRpbXMgPSAxOjExKQpvbGlnb3MuaW50ZWdyYXRlZERUQSA8LSBSdW5UU05FKG9saWdvcy5pbnRlZ3JhdGVkRFRBLCBkaW1zID0gMToxMSkKcGxvdHMgPC0gRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZERUQSwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQpwbG90cyA8LSBsYXBwbHkoWCA9IHBsb3RzLCBGVU4gPSBmdW5jdGlvbih4KSB4ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAzLCAKICAgIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSkKQ29tYmluZVBsb3RzKHBsb3RzKQpwbG90cyA8LSBUU05FUGxvdChvbGlnb3MuaW50ZWdyYXRlZERUQSwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQpwbG90cyA8LSBsYXBwbHkoWCA9IHBsb3RzLCBGVU4gPSBmdW5jdGlvbih4KSB4ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAzLCAKICAgIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSkKQ29tYmluZVBsb3RzKHBsb3RzKQpgYGAgIAoKYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkRFRBIDwtIEZpbmROZWlnaGJvcnMob2xpZ29zLmludGVncmF0ZWREVEEsIGRpbXMgPSAxOjExKQpvbGlnb3MuaW50ZWdyYXRlZERUQSA8LSBGaW5kQ2x1c3RlcnMob2xpZ29zLmludGVncmF0ZWREVEEsYWxnb3JpdGhtID0gNCxyZXNvbHV0aW9uID0gMC42KQpgYGAKCmBgYHtyfQpEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkRFRBLCBncm91cC5ieSA9IGMoInNldXJhdF9jbHVzdGVycyIpLCBjb21iaW5lID0gRkFMU0UpCkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWREVEEsIGdyb3VwLmJ5ID0gYygicHJlZGljdGVkLmlkIiksIGNvbWJpbmUgPSBGQUxTRSkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZERUQSwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQpgYGAKCkZyb20gdGhlIFVNQVAgaXQgc2VlbXMgdGhhdCB0aGVzZSAxNTEgZ2VuZXMgZG8gYWxsb3cgdXMgdG8gc2VwYXJhdGUgdGhlIG1ham9yIE9MIGNsdXN0ZXJzLCBhbmQgdGhlIFVNQVAgc2VlbXMgdG8gcGxhY2UgdGhlIE9QQy1DT1AtTkZPTDEtTkZPTDIgcHJvZ3Jlc3Npb24gY29ycmVjdGx5LiAKClRoZSBEVEEgZ2VuZXMgYWxzbyBzZWVtIHRvIGFsbG93IHVzIHRvIGNsdXN0ZXIgdGhlIE1PTHMgYW5kIGV2ZW4gdGhlIE9QQ3MuCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQojIGZpbmQgbWFya2VycyBmb3IgZXZlcnkgY2x1c3RlciBjb21wYXJlZCB0byBhbGwgcmVtYWluaW5nIGNlbGxzLCByZXBvcnQgb25seSB0aGUgcG9zaXRpdmUgb25lcwpsaWJyYXJ5KGRwbHlyKQpvbGlnb3MuaW50ZWdyYXRlZERUQS5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkRFRBLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCBsb2dmYy50aHJlc2hvbGQgPSAwLjI1KQpgYGAKYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkRFRBLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMiwgd3QgPSBhdmdfbG9nRkMpCmBgYApCZWxvdyBmb2xsb3dzIHRoZSBoZWF0bWFwIHNob3dpbmcgdGhlIHRvcCAxMCBnZW5lcyBiYXNlZCBvbiBmb2xkIGNoYW5nZSBmb3IgZWFjaCBjbHVzdGVyLiAgCmBgYHtyIGZpZy53aWR0aD0xMH0KRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkRFRBKSA8LSAiU0NUIgp0b3AxMCA8LSBvbGlnb3MuaW50ZWdyYXRlZERUQS5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDEwLCB3dCA9IGF2Z19sb2dGQykKRG9IZWF0bWFwKG9saWdvcy5pbnRlZ3JhdGVkRFRBLCBmZWF0dXJlcyA9IHRvcDEwJGdlbmUpICsgTm9MZWdlbmQoKQpgYGAKQW5kIGhlcmUgYXJlIHRoZSB0b3AgMiBnZW5lcyBmb3VuZCBmb3IgZWFjaCBjbHVzdGVyIGFzIHNob3duIG9uIHRoZSBVTUFQLgpgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0KRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkRFRBKSA8LSAiU0NUIgojIE5vcm1hbGl6ZSBSTkEgZGF0YSBmb3IgdmlzdWFsaXphdGlvbiBwdXJwb3Nlcwojb2xpZ29zLmludGVncmF0ZWQgPC0gTm9ybWFsaXplRGF0YShvbGlnb3MuaW50ZWdyYXRlZCwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyIDwtIG9saWdvcy5pbnRlZ3JhdGVkRFRBLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMiwgd3QgPSBhdmdfbG9nRkMpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkRFRBLCBmZWF0dXJlcyA9IHRvcDIkZ2VuZSxwdC5zaXplID0gMC4xKQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWREVEEpIDwtICJTQ1QiCmBgYApIZXJlIEkgc2hvdyBleHByZXNzaW9uIG9mIHNvbWUgY29tbW9uIERUQSBnZW5lcyB0aGF0IEkga25vdyBhcmUgc3VwcG9zZWQgdG8gYmUgbW9yZSBvciBsZXNzIGFmZmVjdGVkLCBiYXNlZCBvbiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIGFuZCB0aGUgY29ubmVjdGVkbmVzcyBpbiB0aGUgU1RSSU5HZGIgbmV0d29yay4KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWREVEEpIDwtICJTQ1QiCiMgTm9ybWFsaXplIFJOQSBkYXRhIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzCiNvbGlnb3MuaW50ZWdyYXRlZCA8LSBOb3JtYWxpemVEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLCB2ZXJib3NlID0gRkFMU0UpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkRFRBLCBjKCJJdG0yYiIsIkFwcCIsICJNYXB0IiwiVHJmIiwiWXdoYXEiLCJLaWYxYiIsIlR1YmExYSIsICJEeW5jMWgxIiwiRHN0IiwiQW5rMiIsICJBbmszIiwgIk5mYXNjIiwiQ250bjIiLCJUcHBwIiwiTmNhbTEiLCJNYnAiLCJDYXIyIiwiVWJiIiwiUHJkeDEiLCJGdGgxIiwiVmRhYzIiLCJBdHA1ZjEiLCJTZXBwMSIsIkhvcHgiLCJPcGFsaW4iLCJQdGdkcyIsIklsMzMiKSxwdC5zaXplID0gMC4xKQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWREVEEpIDwtICJTQ1QiCmBgYApGb3IgcmVmZXJlbmNlIGFuZCB0byBjaGVjayB3aGF0IHRoZSBleHByZXNzaW9uIG9mIGhlYWx0aHkgT0xzIHNob3VsZCBsb29rIGxpa2UgZm9yIHRoZXNlIERUQSBnZW5lcywgd2UgbWFrZSBhIG5ldyBvYmplY3Qgb2YgdGhlIFNjaWVuY2UgZGF0YXNldCAod2l0aG91dCBPUEMgYW5kIENPUCkgYW5kIHVzZSBvbmx5IHRoZSBEVEEgZ2VuZXMgdG8gbWFrZSBhIHRzbmUgYW5kIFVNQVAgYW5kIGNsdXN0ZXIgdGhlbSwgYW5kIGdldCB0aGUgbWFya2Vycy4KR2VuZXJhdGluZyB0aGUgZGF0YXNldCwgVU1BUCwgYW5kIFRTTkUuCmBgYHtyfQpvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UgPC0gc3Vic2V0KFNjaWVuY2UsY2VsbF9jbGFzcyAlaW4lIGMoIk5GT0wxIiwiTkZPTDIiLCJNRk9MMSIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IikpCm9saWdvcy5pbnRlZ3JhdGVkU2NpZW5jZSA8LSBSdW5QQ0Eob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCB2ZXJib3NlID0gRkFMU0UsZmVhdHVyZXM9ZmVhdHVyZXNEVEEpCkVsYm93UGxvdChvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UpCmBgYApgYGB7cn0Kb2xpZ29zLmludGVncmF0ZWRTY2llbmNlIDwtIFJ1blVNQVAob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBkaW1zID0gMToxMyxuLm5laWdoYm9ycyA9IDIwKQojb2xpZ29zLmludGVncmF0ZWRTY2llbmNlIDwtIFJ1blRTTkUob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBkaW1zID0gMToxMCkKcGxvdHMgPC0gRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UsIGdyb3VwLmJ5ID0gYygiY2VsbF9jbGFzcyIpLCBjb21iaW5lID0gRkFMU0UpCnBsb3RzIDwtIGxhcHBseShYID0gcGxvdHMsIEZVTiA9IGZ1bmN0aW9uKHgpIHggKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMsIAogICAgYnlyb3cgPSBUUlVFLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpKQpDb21iaW5lUGxvdHMocGxvdHMpCiMgcGxvdHMgPC0gVFNORVBsb3Qob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBncm91cC5ieSA9IGMoImNlbGxfY2xhc3MiKSwgY29tYmluZSA9IEZBTFNFKQojIHBsb3RzIDwtIGxhcHBseShYID0gcGxvdHMsIEZVTiA9IGZ1bmN0aW9uKHgpIHggKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMsIAojICAgICBieXJvdyA9IFRSVUUsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkpCiMgQ29tYmluZVBsb3RzKHBsb3RzKQpgYGAgIApgYGB7cn0Kb2xpZ29zLmludGVncmF0ZWRTY2llbmNlIDwtIEZpbmROZWlnaGJvcnMob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBkaW1zID0gMToxMykKb2xpZ29zLmludGVncmF0ZWRTY2llbmNlIDwtIEZpbmRDbHVzdGVycyhvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UsYWxnb3JpdGhtID0gNCxyZXNvbHV0aW9uID0gMC42KQpgYGAKYGBge3J9CkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBncm91cC5ieSA9IGMoInNldXJhdF9jbHVzdGVycyIpLCBjb21iaW5lID0gRkFMU0UpCkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBncm91cC5ieSA9IGMoImNlbGxfY2xhc3MiKSwgY29tYmluZSA9IEZBTFNFKQpgYGAKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyBmaW5kIG1hcmtlcnMgZm9yIGV2ZXJ5IGNsdXN0ZXIgY29tcGFyZWQgdG8gYWxsIHJlbWFpbmluZyBjZWxscywgcmVwb3J0IG9ubHkgdGhlIHBvc2l0aXZlIG9uZXMKbGlicmFyeShkcGx5cikKb2xpZ29zLmludGVncmF0ZWRTY2llbmNlLm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCBsb2dmYy50aHJlc2hvbGQgPSAwLjI1KQpgYGAKYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkU2NpZW5jZS5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDIsIHd0ID0gYXZnX2xvZ0ZDKQpgYGAKQmVsb3cgZm9sbG93cyB0aGUgaGVhdG1hcCBzaG93aW5nIHRoZSB0b3AgMTAgZ2VuZXMgYmFzZWQgb24gZm9sZCBjaGFuZ2UgZm9yIGVhY2ggY2x1c3Rlci4gIApgYGB7ciBmaWcud2lkdGg9MTB9CkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UpIDwtICJTQ1QiCnRvcDEwIDwtIG9saWdvcy5pbnRlZ3JhdGVkU2NpZW5jZS5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDEwLCB3dCA9IGF2Z19sb2dGQykKRG9IZWF0bWFwKG9saWdvcy5pbnRlZ3JhdGVkU2NpZW5jZSwgZmVhdHVyZXMgPSB0b3AxMCRnZW5lKSArIE5vTGVnZW5kKCkKYGBgCkFuZCBoZXJlIGFyZSB0aGUgdG9wIDIgZ2VuZXMgZm91bmQgZm9yIGVhY2ggY2x1c3RlciBhcyBzaG93biBvbiB0aGUgVU1BUC4KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWRTY2llbmNlKSA8LSAiU0NUIgojIE5vcm1hbGl6ZSBSTkEgZGF0YSBmb3IgdmlzdWFsaXphdGlvbiBwdXJwb3Nlcwojb2xpZ29zLmludGVncmF0ZWQgPC0gTm9ybWFsaXplRGF0YShvbGlnb3MuaW50ZWdyYXRlZCwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyIDwtIG9saWdvcy5pbnRlZ3JhdGVkU2NpZW5jZS5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDIsIHd0ID0gYXZnX2xvZ0ZDKQpGZWF0dXJlUGxvdChvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UsIGZlYXR1cmVzID0gdG9wMiRnZW5lLHB0LnNpemUgPSAwLjEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UpIDwtICJTQ1QiCmBgYApIZXJlIEkgc2hvdyBleHByZXNzaW9uIG9mIHNvbWUgY29tbW9uIERUQSBnZW5lcyB0aGF0IEkga25vdyBhcmUgc3VwcG9zZWQgdG8gYmUgbW9yZSBvciBsZXNzIGFmZmVjdGVkLCB0byBjb21wYXJlZCB0aGVtIHdpdGggdGhlIGV4cHJlc3Npb24gb2YgdGhlIERUQSBkYXRhc2V0IGFib3ZlLgpgYGB7ciBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTB9CkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UpIDwtICJTQ1QiCiMgTm9ybWFsaXplIFJOQSBkYXRhIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzCiNvbGlnb3MuaW50ZWdyYXRlZCA8LSBOb3JtYWxpemVEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLCB2ZXJib3NlID0gRkFMU0UpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkU2NpZW5jZSwgYygiSXRtMmIiLCJTY2QyIiwiQXBwIiwgIk1hcHQiLCJUcmYiLCJZd2hhcSIsIktpZjFiIiwiVHViYTFhIiwgIkR5bmMxaDEiLCJEc3QiLCJBbmsyIiwgIkFuazMiLCAiTmZhc2MiLCJDbnRuMiIsIlRwcHAiLCJOY2FtMSIsIk1icCIsIkNhcjIiLCJVYmIiLCJQcmR4MSIsIkZ0aDEiLCJWZGFjMiIsIkF0cDVmMSIsIlNlcHAxIiwiSG9weCIsIk9wYWxpbiIsIlB0Z2RzIiwiSWwzMyIsIlNlcnBpbmIxYSIsIkhhcGxuMiIsIlJhYjM3IikscHQuc2l6ZSA9IDEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UpIDwtICJTQ1QiCmBgYApOb3cgd2Ugd2lsbCBzdGFydCB0byBhbmFseXNlIHRoZSBEVEEgZ2VuZXMgdGhhdCB3ZSBmb3VuZCBlYXJsaWVyLiBIZXJlIEkgY2hvc2UgdGhlIHJlYWN0b21lIGRhdGFiYXNlLCBiZWNhdXNlIGl0IHNlZW1zIHRvIGdpdmUgbWUgYWN0dWFsIHBhdGh3YXlzIHRoYXQgbWlnaHQgYmUgYWZmZWN0ZWQuCgpgYGB7cn0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCiNDb252ZXJ0IHRvIGdlbmNvZGUgdXNpbmcgYmlvbWFydApsaWJyYXJ5KGJpb21hUnQpCkRUQU9MIDwtIHN1YnNldChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MNVJOQSwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC4xKQpEVEFPTCA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZkRUQTIzdnMxNVJOQSwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMCkKRFRBT0wgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZPUENSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMSkKRFRBT0wkR2VuZSA8LSByb3cubmFtZXMoRFRBT0wpCkRUQU9MIDwtIEFsbGRpZmYKZ2VuZW1vZHVsZXNHTyA8LSBEVEFPTFshZHVwbGljYXRlZChEVEFPTCRHZW5lKSxdCmxpc3RNYXJ0cygpCmVuc2VtYmwgPSB1c2VNYXJ0KCJlbnNlbWJsIixkYXRhc2V0PSJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIikKbGlzdERhdGFzZXRzKGVuc2VtYmwpCmF0dHJpYnV0ZXMgPSBsaXN0QXR0cmlidXRlcyhlbnNlbWJsKQpCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzIDwtIGdldEJNKGF0dHJpYnV0ZXM9YygibWdpX3N5bWJvbCIsImVuc2VtYmxfZ2VuZV9pZCIsImVudHJlemdlbmVfaWQiLCJnZW5lX2Jpb3R5cGUiKSwgZmlsdGVycyA9ICIiLCB2YWx1ZXMgPSAiIiwgZW5zZW1ibCkKQmlvbWFydF9nZW5jb2RlX2Vuc2VtYmw4NF9iaW90eXBlc1ssICdnZW5lX2Jpb3R5cGUnXSA8LSBhcy5mYWN0b3IoQmlvbWFydF9nZW5jb2RlX2Vuc2VtYmw4NF9iaW90eXBlc1ssJ2dlbmVfYmlvdHlwZSddKQojRmlsdGVyIGZvciBvbmx5IG91ciBnZW5lcwogQmlvdHlwZV9BbGxfZGF0YXNldCA8LSBzdWJzZXQoQmlvbWFydF9nZW5jb2RlX2Vuc2VtYmw4NF9iaW90eXBlcywgbWdpX3N5bWJvbCAlaW4lIG9saWdvcy5pbnRlZ3JhdGVkQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKQplbnRyZXpJRCA8LSAgc3Vic2V0KEJpb3R5cGVfQWxsX2RhdGFzZXQsIEJpb3R5cGVfQWxsX2RhdGFzZXQkbWdpX3N5bWJvbCAlaW4lIG9saWdvcy5pbnRlZ3JhdGVkQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKQplbnRyZXptYXRjaGVkIDwtIGVudHJleklEW21hdGNoKGdlbmVtb2R1bGVzR08kR2VuZSxlbnRyZXpJRCRtZ2lfc3ltYm9sKSxdCmVudHJleklEIDwtIGVudHJleklEWyEgYXBwbHkoZW50cmV6SURbLGMoMSwzKV0sIDEsZnVuY3Rpb24gKHgpIGFueU5BKHgpKSxdCmFsbExMSURzIDwtIGVudHJlem1hdGNoZWQkZW50cmV6Z2VuZQpgYGAKYGBge3J9CmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQojQ29udmVydCB0byBnZW5jb2RlIHVzaW5nIGJpb21hcnQKbGlicmFyeShiaW9tYVJ0KQojU3Vic2V0IHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBnZW5lbGlzdCBmcm9tIGEgc2V1cmF0IGRpZmYgZXhwcmVzc2lvbiByZXN1bHQgd2l0aCB0aGUgcGFyYW1ldGVycyB5b3UgdXNlLgpEVEFPTCA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZk1PTDVSTkEsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMSkKRFRBT0wkR2VuZSA8LSByb3cubmFtZXMoRFRBT0wpCiNyZW1vdmUgYW55IGR1cGxpY2F0ZXMgKHNhbml0eSBjaGVjayBmb3IgbWUpCmdlbmVtb2R1bGVzR08gPC0gRFRBT0xbIWR1cGxpY2F0ZWQoRFRBT0wkR2VuZSksXQoKI0NvbnZlcnQgdG8gZW50cmV6Cmxpc3RNYXJ0cygpCmVuc2VtYmwgPSB1c2VNYXJ0KCJlbnNlbWJsIixkYXRhc2V0PSJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIikKbGlzdERhdGFzZXRzKGVuc2VtYmwpCmF0dHJpYnV0ZXMgPSBsaXN0QXR0cmlidXRlcyhlbnNlbWJsKQpCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzIDwtIGdldEJNKGF0dHJpYnV0ZXM9YygibWdpX3N5bWJvbCIsImVuc2VtYmxfZ2VuZV9pZCIsImVudHJlemdlbmVfaWQiLCJnZW5lX2Jpb3R5cGUiKSwgZmlsdGVycyA9ICIiLCB2YWx1ZXMgPSAiIiwgZW5zZW1ibCkKQmlvbWFydF9nZW5jb2RlX2Vuc2VtYmw4NF9iaW90eXBlc1ssICdnZW5lX2Jpb3R5cGUnXSA8LSBhcy5mYWN0b3IoQmlvbWFydF9nZW5jb2RlX2Vuc2VtYmw4NF9iaW90eXBlc1ssJ2dlbmVfYmlvdHlwZSddKQojRmlsdGVyIGZvciBvbmx5IG91ciBnZW5lcwogQmlvdHlwZV9BbGxfZGF0YXNldCA8LSBzdWJzZXQoQmlvbWFydF9nZW5jb2RlX2Vuc2VtYmw4NF9iaW90eXBlcywgbWdpX3N5bWJvbCAlaW4lIG9saWdvcy5pbnRlZ3JhdGVkQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKQplbnRyZXpJRCA8LSAgc3Vic2V0KEJpb3R5cGVfQWxsX2RhdGFzZXQsIEJpb3R5cGVfQWxsX2RhdGFzZXQkbWdpX3N5bWJvbCAlaW4lIG9saWdvcy5pbnRlZ3JhdGVkQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKQplbnRyZXptYXRjaGVkIDwtIGVudHJleklEW21hdGNoKGdlbmVtb2R1bGVzR08kR2VuZSxlbnRyZXpJRCRtZ2lfc3ltYm9sKSxdCiN5b3UgbWlnaHQgbmVlZCB0byByZW1vdmUgTkFzCiMjZW50cmV6SUQgPC0gZW50cmV6SURbISBhcHBseShlbnRyZXpJRFssYygxLDMpXSwgMSxmdW5jdGlvbiAoeCkgYW55TkEoeCkpLF0KYWxsTExJRHMgPC0gZW50cmV6bWF0Y2hlZCRlbnRyZXpnZW5lCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KFJlYWN0b21lUEEpCmxpYnJhcnkob3JnLk1tLmVnLmRiKQptb2R1bGVzUmVhY3RvbWVPUEMgPC0gZW5yaWNoUGF0aHdheShnZW5lPWFsbExMSURzLG9yZ2FuaXNtPSJtb3VzZSIscHZhbHVlQ3V0b2ZmPTAuMDEscXZhbHVlQ3V0b2ZmID0gMC4zLHBBZGp1c3RNZXRob2QgPSAibm9uZSIsIHJlYWRhYmxlPVQpCmhlYWQoYXMuZGF0YS5mcmFtZShtb2R1bGVzUmVhY3RvbWUpKQpgYGAKIyMgUmVhY3RvbWUgQW5hbHlzaXMgeyNsMWNhbX0KYGBge3IgZmlnLndpZHRoPTV9CmRvdHBsb3QobW9kdWxlc1JlYWN0b21lLCBzaG93Q2F0ZWdvcnk9MjApCmBgYApgYGB7ciBmaWcud2lkdGg9Nn0KZW1hcHBsb3QobW9kdWxlc1JlYWN0b21lKQpgYGAKYGBge3IgZmlnLndpZHRoPTEwfQpGQyA8LSBnZW5lbW9kdWxlc0dPJGF2Z19sb2dGQwpuYW1lcyhGQykgPC0gZ2VuZW1vZHVsZXNHTyRHZW5lCmNuZXRwbG90KG1vZHVsZXNSZWFjdG9tZSwgc2hvd0NhdGVnb3J5ID0gMjAsY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBmb2xkQ2hhbmdlPUZDLGNvbG9yRWRnZSA9IFQsbm9kZV9sYWJlbD1ULGNpcmN1bGFyID0gRikKYGBgCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKI0NvbnZlcnQgdG8gZ2VuY29kZSB1c2luZyBiaW9tYXJ0CmxpYnJhcnkoYmlvbWFSdCkKT1BDX2RpZmYKT1BDX2RpZmYkR2VuZSA8LSByb3cubmFtZXMoT1BDX2RpZmYpCmdlbmVtb2R1bGVzR08gPC0gT1BDX2RpZmYKbGlzdE1hcnRzKCkKZW5zZW1ibCA9IHVzZU1hcnQoImVuc2VtYmwiLGRhdGFzZXQ9Im1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQpsaXN0RGF0YXNldHMoZW5zZW1ibCkKYXR0cmlidXRlcyA9IGxpc3RBdHRyaWJ1dGVzKGVuc2VtYmwpCkJpb21hcnRfZ2VuY29kZV9lbnNlbWJsODRfYmlvdHlwZXMgPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCJtZ2lfc3ltYm9sIiwiZW5zZW1ibF9nZW5lX2lkIiwiZW50cmV6Z2VuZV9pZCIsImdlbmVfYmlvdHlwZSIpLCBmaWx0ZXJzID0gIiIsIHZhbHVlcyA9ICIiLCBlbnNlbWJsKQpCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzWywgJ2dlbmVfYmlvdHlwZSddIDwtIGFzLmZhY3RvcihCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzWywnZ2VuZV9iaW90eXBlJ10pCiNGaWx0ZXIgZm9yIG9ubHkgb3VyIGdlbmVzCiBCaW90eXBlX0FsbF9kYXRhc2V0IDwtIHN1YnNldChCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzLCBtZ2lfc3ltYm9sICVpbiUgb2xpZ29zLmludGVncmF0ZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpCmVudHJleklEIDwtICBzdWJzZXQoQmlvdHlwZV9BbGxfZGF0YXNldCwgQmlvdHlwZV9BbGxfZGF0YXNldCRtZ2lfc3ltYm9sICVpbiUgb2xpZ29zLmludGVncmF0ZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpCmVudHJlem1hdGNoZWQgPC0gZW50cmV6SURbbWF0Y2goZ2VuZW1vZHVsZXNHTyRHZW5lLGVudHJleklEJG1naV9zeW1ib2wpLF0KZW50cmV6SUQgPC0gZW50cmV6SURbISBhcHBseShlbnRyZXpJRFssYygxLDMpXSwgMSxmdW5jdGlvbiAoeCkgYW55TkEoeCkpLF0KYWxsTExJRHMgPC0gZW50cmV6bWF0Y2hlZCRlbnRyZXpnZW5lCgpgYGAKCmBgYHtyfQpsaWJyYXJ5KFJlYWN0b21lUEEpCmxpYnJhcnkob3JnLk1tLmVnLmRiKQptb2R1bGVzUmVhY3RvbWUgPC0gZW5yaWNoUGF0aHdheShnZW5lPWFsbExMSURzLG9yZ2FuaXNtPSJtb3VzZSIscHZhbHVlQ3V0b2ZmPTAuMDEscXZhbHVlQ3V0b2ZmID0gMC4zLHBBZGp1c3RNZXRob2QgPSAibm9uZSIsIHJlYWRhYmxlPVQpCmhlYWQoYXMuZGF0YS5mcmFtZShtb2R1bGVzUmVhY3RvbWUpKQpgYGAKYGBge3IgZmlnLndpZHRoPTd9CmRvdHBsb3QobW9kdWxlc1JlYWN0b21lLCBzaG93Q2F0ZWdvcnk9OCkKYGBgCmBgYHtyIGZpZy53aWR0aD02fQplbWFwcGxvdChtb2R1bGVzUmVhY3RvbWUpCmBgYApgYGB7ciBmaWcud2lkdGg9OH0KRkMgPC0gZ2VuZW1vZHVsZXNHTyRhdmdfbG9nRkMKbmFtZXMoRkMpIDwtIGdlbmVtb2R1bGVzR08kR2VuZQpjbmV0cGxvdChtb2R1bGVzUmVhY3RvbWUsIHNob3dDYXRlZ29yeSA9IDgsY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBmb2xkQ2hhbmdlPUZDKQpgYGAKIyBMYWJlbCB0cmFuc2ZlciB3aXRoIFNjaWVuY2UgZGF0YXNldC4KICAKTm93IHRvIHRyYW5zZmVyIHRoZSBsYWJlbHMgb2YgdGhlIFNjaWVuY2UgZGF0YXNldCwgd2Ugd2lsbCBpbnRlZ3JhdGUgYWxsIHRoZSBkYXRhc2V0cyB0b2dldGhlciBhbmQgdHJ5IHRvIHByZWRpY3QgdGhlIGNsdXN0ZXIgbWVtYmVyc2hpcCBvZiB0aGUgMTBYIGRhdGEuIChTb21lIGNvZGUgaXMgaGlkZGVuIHRvIGtlZXAgaXQgc3RyZWFtbGluZWQpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDQwMDAgKiAxMDI0XjIpCiNMb2FkIGRhdGEKbG9hZCgifi9Eb2N1bWVudHMvU2luZ2xlQ2VsbERhdGEvTmV0d29ya2NsdXN0ZXJpbmcvRWxpc2FBbmFseXNpcy9FdmVyeXRoaW5nQ29tYmluZWQuUmRhdGEiKQpjZWxsc3RvdXNlIDwtIGludGVyc2VjdChjb2xuYW1lcyhlbWF0XzEweCkscm93Lm5hbWVzKGFubm9fMTB4KSkKZW1hdF8xMHggPC0gZW1hdF8xMHhbLGNlbGxzdG91c2VdCmFubm9fMTB4IDwtIGFubm9fMTB4W2NlbGxzdG91c2UsXQp0YWJsZShhbm5vXzEweCkKI1NSZW1vdmUgYmFkIHNhbXBsZSA0NyBmcm9tIGRhdGEKZW1hdF8xMHggPC0gZW1hdF8xMHhbLGFubm9fMTB4ICVpbiUgYygiVENfNTAiLCJHQ181MSIsIkdEXzUyIildCmFubm9fMTB4IDwtIGFzLmNoYXJhY3Rlcihhbm5vXzEweCkKbmFtZXMoYW5ub18xMHgpIDwtIGNlbGxzdG91c2UKYW5ub18xMHggPC0gYW5ub18xMHhbY29sbmFtZXMoZW1hdF8xMHgpXQojY29sbmFtZXMoYW5ub18xMHgpIDwtICJTYW1wbGUiCmFubm9fMTB4IDwtIGFzLmRhdGEuZnJhbWUoYW5ub18xMHgsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpjb2xuYW1lcyhhbm5vXzEweCkgPC0gIlNhbXBsZSIKI1B1dCBpbiBTZXVyYXQgb2JqZWN0IGFuZCBzcGxpdCBpbiB0d28gdG8gcGVyZm9ybSBwcmVwbm9ybWFsaXphdGlvbgpvbGlnb3MgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGVtYXRfMTB4LCBtZXRhLmRhdGEgPSAgYW5ub18xMHgsbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQpvbGlnb3NbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KG9saWdvcywgcGF0dGVybiA9ICJebXQtIikKb2xpZ29zIDwtIHN1YnNldChvbGlnb3MsIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDIwMCAmIG5GZWF0dXJlX1JOQSA8IDMwMDAgJiBwZXJjZW50Lm10IDwgMTUpCmBgYApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KI29saWdvcy5pbnRlZ3JhdGVkLkRUQSA8LSBTQ1RyYW5zZm9ybShvbGlnb3MsdmVyYm9zZSA9IEZBTFNFKQpvbGlnb3MubGlzdCA8LSBTcGxpdE9iamVjdChvbGlnb3MsIHNwbGl0LmJ5ID0gIlNhbXBsZSIpCmZvciAoaSBpbiAxOmxlbmd0aChvbGlnb3MubGlzdCkpIHsKICAgIG9saWdvcy5saXN0W1tpXV0gPC0gU0NUcmFuc2Zvcm0ob2xpZ29zLmxpc3RbW2ldXSwgdmVyYm9zZSA9IEZBTFNFKQp9CiMgCiMgbG9hZCgifi9Eb2N1bWVudHMvU2luZ2xlQ2VsbERhdGEvU2NpZW5jZWRhdGFzZXQvU2NpZW5jZW1hdHJpY2VzYW5uby5SZGF0YSIpCiMgYW5ub19zY2llbmNlJFNhbXBsZSA8LSByZXAoIlNjaWVuY2UiLG5jb2woZW1hdF9zY2llbmNlKSkKIyBTY2llbmNlIDwtIENyZWF0ZVNldXJhdE9iamVjdChlbWF0X3NjaWVuY2UsIG1ldGEuZGF0YSA9ICBhbm5vX3NjaWVuY2UsbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQojIG9saWdvcy5saXN0IDwtIGxpc3QoKQojIG9saWdvcy5saXN0W1sxXV0gPC0gb2xpZ29zLmludGVncmF0ZWQuRFRBCm9saWdvcy5saXN0W1tsZW5ndGgob2xpZ29zLmxpc3QpKzFdXSA8LSBTQ1RyYW5zZm9ybShTY2llbmNlLCBtaW5fY2VsbHM9Myx2ZXJib3NlID0gRkFMU0UpCmBgYApgYGB7ciBpbmNsdWRlPUZBTFNFfQojaW50ZWdyYXRlCm9saWdvcy5mZWF0dXJlcyA8LSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKG9iamVjdC5saXN0ID0gb2xpZ29zLmxpc3QsIG5mZWF0dXJlcyA9IDMwMDApCm9saWdvcy5saXN0IDwtIFByZXBTQ1RJbnRlZ3JhdGlvbihvYmplY3QubGlzdCA9IG9saWdvcy5saXN0LCBhbmNob3IuZmVhdHVyZXMgPSBvbGlnb3MuZmVhdHVyZXMsIAogICAgdmVyYm9zZSA9IEZBTFNFKQpvbGlnb3MuYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gb2xpZ29zLmxpc3QsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIAogICAgYW5jaG9yLmZlYXR1cmVzID0gb2xpZ29zLmZlYXR1cmVzLCB2ZXJib3NlID0gRkFMU0UpCm9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBvbGlnb3MuYW5jaG9ycywgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwgCiAgICB2ZXJib3NlID0gRkFMU0UpCmBgYAogIApHZW5lcmF0aW5nIHRoZSBVTUFQIGFuZCBUU05FLCBmb3IgdGhlIGludGVncmF0ZWQgZGF0YXNldCB3aXRoIHRoZSBTY2llbmNlIGRhdGFzZXQuCmBgYHtyfQpvbGlnb3MuaW50ZWdyYXRlZC5mdWxsIDwtIFJ1blBDQShvbGlnb3MuaW50ZWdyYXRlZC5mdWxsLCB2ZXJib3NlID0gRkFMU0UpCkVsYm93UGxvdChvbGlnb3MuaW50ZWdyYXRlZC5mdWxsKQpgYGAKYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwgPC0gUnVuVU1BUChvbGlnb3MuaW50ZWdyYXRlZC5mdWxsLCBkaW1zID0gMTozMCkKI29saWdvcy5pbnRlZ3JhdGVkIDwtIFJ1blRTTkUob2xpZ29zLmludGVncmF0ZWQsIGRpbXMgPSAxOjMwKQpwbG90cyA8LSBEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwsIGdyb3VwLmJ5ID0gYygiU2FtcGxlIiksIGNvbWJpbmUgPSBGQUxTRSkKcGxvdHMgPC0gbGFwcGx5KFggPSBwbG90cywgRlVOID0gZnVuY3Rpb24oeCkgeCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMywgCiAgICBieXJvdyA9IFRSVUUsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkpCkNvbWJpbmVQbG90cyhwbG90cykKIyBwbG90cyA8LSBUU05FUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQojIHBsb3RzIDwtIGxhcHBseShYID0gcGxvdHMsIEZVTiA9IGZ1bmN0aW9uKHgpIHggKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMsCiMgICAgIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSkKIyBDb21iaW5lUGxvdHMocGxvdHMpCmBgYApIZXJlIEkgcmVwZWF0IHRoZSBwbG90dGluZyBvZiBleHByZXNzaW9uIG9mIHNvbWUgY29tbW9uIGdlbmVzIHRoYXQgSSBrbm93IGFyZSBzdXBwb3NlZCB0byBiZSBtb3JlIG9yIGxlc3Mgc3RhYmxlIGNsdXN0ZXJzIHdpdGhpbiB0aGUgT0xzLCBqdXN0IGZvciByZWZlcmVuY2UuCmBgYHtyIGZpZy53aWR0aD0xMH0KRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwpIDwtICJSTkEiCiNOb3JtYWxpemUgUk5BIGRhdGEgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMKb2xpZ29zLmludGVncmF0ZWQuZnVsbCA8LSBOb3JtYWxpemVEYXRhKG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwsIHZlcmJvc2UgPSBGQUxTRSkKRmVhdHVyZVBsb3Qob2xpZ29zLmludGVncmF0ZWQuZnVsbCwgYygiUGRnZnJhIiwgIlRvcDJhIiwiUHRwcnoxIiwiQm1wNCIsIkl0cHIyIiwgIkVncjEiLCAiS2xrNiIsICJIb3B4IiwgIlB0Z2RzIiwiSWwzMyIpLHB0LnNpemUgPSAwLjEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZC5mdWxsKSA8LSAiaW50ZWdyYXRlZCIKYGBgCkhlcmUgSSBzZXQgdGhlIGNsdXN0ZXJpbmcgcmVzb2x1dGlvbiBoaWdoIGVub3VnaCB0byBpbmNsdWRlIHRoZSBDT1BzLCB0aGlzIG1lYW5zIHRoYXQgdGhlIE1PTHMgYXJlIGJyb2tlbiBpbnRvIG1vcmUgY2x1c3RlcnMgdGhhbiBpbiB0aGUgc2NpZW5jZSBwYXBlci4gICAKSSBzaG93IHRoZSBjbHVzdGVycyBvbiB0aGUgVU1BUCBzbyB5b3UgY2FuIHNlZSB0aGVpciBwb3NpdGlvbi4KYGBge3J9Cm9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwgPC0gRmluZE5laWdoYm9ycyhvbGlnb3MuaW50ZWdyYXRlZC5mdWxsLCBkaW1zID0gMTozMCkKb2xpZ29zLmludGVncmF0ZWQuZnVsbCA8LSBGaW5kQ2x1c3RlcnMob2xpZ29zLmludGVncmF0ZWQuZnVsbCxhbGdvcml0aG0gPSA0LHJlc29sdXRpb24gPSAwLjYpCmBgYApgYGB7cn0KRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZC5mdWxsLCBncm91cC5ieSA9IGMoInNldXJhdF9jbHVzdGVycyIpLCBjb21iaW5lID0gRkFMU0UpCmBgYApCZWxvdyB5b3Ugd2lsbCBmaW5kIGEgdGFibGUgb2YgdGhlIHRvcCAyIG1hcmtlcnMgZm91bmQgZm9yIGVhY2ggY2x1c3Rlci4gcGN0IG1lYW5zIHBlcmNlbnRhZ2Ugb2YgZXhwcmVzc2lvbiwgd2hlcmUgcGN0LjIgcmVmZXJzIHRvIGFsbCB0aGUgY2VsbHMgbm90IGluIHRoZSB0ZXN0ZWQgY2x1c3Rlci4KYGBge3IgaW5jbHVkZT1GQUxTRX0KRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwpIDwtICJpbnRlZ3JhdGVkIgojIGZpbmQgbWFya2VycyBmb3IgZXZlcnkgY2x1c3RlciBjb21wYXJlZCB0byBhbGwgcmVtYWluaW5nIGNlbGxzLCByZXBvcnQgb25seSB0aGUgcG9zaXRpdmUgb25lcwpsaWJyYXJ5KGRwbHlyKQpvbGlnb3MuaW50ZWdyYXRlZC5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwsIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUpCmBgYApgYGB7cn0Kb2xpZ29zLmludGVncmF0ZWQubWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG4gPSAyLCB3dCA9IGF2Z19sb2dGQykKYGBgCkJlbG93IGZvbGxvd3MgdGhlIGhlYXRtYXAgc2hvd2luZyB0aGUgdG9wIDEwIGdlbmVzIGJhc2VkIG9uIGZvbGQgY2hhbmdlIGZvciBlYWNoIGNsdXN0ZXIsIHVzaW5nIHRoZSBTZXVyYXQgZm91bmQgY2x1c3RlciBpbmZvcm1hdGlvbi4gIApgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZC5mdWxsKSA8LSAiaW50ZWdyYXRlZCIKdG9wMTAgPC0gb2xpZ29zLmludGVncmF0ZWQubWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG4gPSAxMCwgd3QgPSBhdmdfbG9nRkMpCkRvSGVhdG1hcChvbGlnb3MuaW50ZWdyYXRlZC5mdWxsLCBmZWF0dXJlcyA9IHRvcDEwJGdlbmUpICsgTm9MZWdlbmQoKQpgYGAKQW5kIGhlcmUgYXJlIHRoZSB0b3AgMiBnZW5lcyBmb3VuZCBmb3IgZWFjaCBjbHVzdGVyIGFzIHNob3duIG9uIHRoZSBVTUFQLgpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTB9CkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZC5mdWxsKSA8LSAiUk5BIgojIE5vcm1hbGl6ZSBSTkEgZGF0YSBmb3IgdmlzdWFsaXphdGlvbiBwdXJwb3NlcwpvbGlnb3MuaW50ZWdyYXRlZC5mdWxsIDwtIE5vcm1hbGl6ZURhdGEob2xpZ29zLmludGVncmF0ZWQuZnVsbCwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyIDwtIG9saWdvcy5pbnRlZ3JhdGVkLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMiwgd3QgPSBhdmdfbG9nRkMpCkZlYXR1cmVQbG90KG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwsIGZlYXR1cmVzID0gdG9wMiRnZW5lLHB0LnNpemUgPSAwLjEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZC5mdWxsKSA8LSAiaW50ZWdyYXRlZCIKYGBgCiMjIyMgTGFiZWwgdHJhbnNmZXIKTm93IHdlIGF0dGVtcHQgdG8gdHJhbnNmZXIgdGhlIGNsdXN0ZXIgbGFiZWxzIG9mIHRoZSBTY2llbmNlIGRhdGFzZXQgb250byB0aGUgMTBYIGRhdGFzZXQuCmBgYHtyfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQuZnVsbCkgPC0gImludGVncmF0ZWQiCm9saWdvcy5xdWVyeSA8LSBvbGlnb3MubGlzdApmb3IgKGkgaW4gMToobGVuZ3RoKG9saWdvcy5xdWVyeSktMSkpIHsKICBwcmludChwYXN0ZSgiV29ya2luZyBvbiBkYXRhc2V0ICIsaSwiIG9mIixsZW5ndGgob2xpZ29zLmxpc3QpKSkKICAgIG9saWdvcy5hbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gb2xpZ29zLmludGVncmF0ZWQuZnVsbCwgcXVlcnkgPW9saWdvcy5saXN0W1tpXV0sIAogICAgZGltcyA9IDE6MTMscmVkdWN0aW9uID0gImNjYSIpIAogICAgcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IG9saWdvcy5hbmNob3JzLCByZWZkYXRhID0gb2xpZ29zLmludGVncmF0ZWQuZnVsbCRjZWxsX2NsYXNzLCAKICAgIGRpbXMgPSAxOjEzLHdlaWdodC5yZWR1Y3Rpb24gPSAiY2NhIikKICAgIG9saWdvcy5xdWVyeVtbaV1dIDwtIEFkZE1ldGFEYXRhKG9saWdvcy5saXN0W1tpXV0sIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCn0KcHJlZGljdGVkLmNlbGxjbGFzcyA8LSBhcy5jaGFyYWN0ZXIoKQpmb3IgKGkgaW4gMToobGVuZ3RoKG9saWdvcy5xdWVyeSktMSkpIHsKcHJlZGljdGVkLmNlbGxjbGFzcyA8LSAgIGMocHJlZGljdGVkLmNlbGxjbGFzcyxvbGlnb3MucXVlcnlbW2ldXSRwcmVkaWN0ZWQuaWQpCn0KCnByZWRpY3RlZC5jZWxsY2xhc3MgPC0gYyhwcmVkaWN0ZWQuY2VsbGNsYXNzLG9saWdvcy5pbnRlZ3JhdGVkLmZ1bGwkY2VsbF9jbGFzc1shaXMubmEob2xpZ29zLmludGVncmF0ZWQuZnVsbCRjZWxsX2NsYXNzKV0pCnRhYmxlKG5hbWVzKHByZWRpY3RlZC5jZWxsY2xhc3MpPT1jb2xuYW1lcyhvbGlnb3MuaW50ZWdyYXRlZC5mdWxsKSkKb2xpZ29zLmludGVncmF0ZWQuZnVsbEBtZXRhLmRhdGEkcHJlZGljdGVkLmNlbGxjbGFzcyA8LSBwcmVkaWN0ZWQuY2VsbGNsYXNzCmBgYAogIApIZXJlIGlzIHRoZSBlbmQgcmVzdWx0IHByb2plY3RlZCBvbiB0aGUgVU1BUC4KYGBge3J9CkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQuZnVsbCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuY2VsbGNsYXNzIiksIGNvbWJpbmUgPSBGQUxTRSkKYGBgCiAgCmBgYHtyfQpEaWZmTWF0cml4IDwtIGxpc3QoKQoKZGlmZm1hdHJpeG5hbWVzIDwtIGMoIm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZBbGxSTkEiLAogICAgICAgICAgICAgICAgICAgICJvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmTU9MNVJOQSIsCiAgICAgICAgICAgICAgICAgICAgIm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZPUENSTkEiLAogICAgICAgICAgICAgICAgICAgICJvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmRFRBMnZzM1JOQSIpCiAgICAgICAgICAgICAgICAgICAgIAoKZG8uY2FsbChoZWFkLGFzLmxpc3QoYXMubmFtZShkaWZmbWF0cml4bmFtZXNbMV0pKSkKYGBgCmBgYHtyfQpsaWJyYXJ5KHhsc3gpCmxpYnJhcnkoc3RyaW5ncikKc2V0d2QoIn4vRG9jdW1lbnRzL1NpbmdsZUNlbGxEYXRhL05ldHdvcmtjbHVzdGVyaW5nL0VsaXNhQW5hbHlzaXMvSW50ZWdyYXRpb25EVEFuZXR3b3JrL0ZpZ3VyZXMvIikKZmlsZSA8LSBwYXN0ZSgiRGlmZmVyZW50aWFsRXhwcmVzc2lvbi54bHN4Iiwgc2VwID0gIiIpCgpmb3IoaSBpbiAxOmxlbmd0aChkaWZmbWF0cml4bmFtZXMpKXsKaWYoaT09MSl7CndyaXRlLnhsc3goYXMuZGF0YS5mcmFtZShnZXQoZGlmZm1hdHJpeG5hbWVzW2ldKSksIGZpbGUsIHNoZWV0TmFtZSA9IHN0cl9zdWIoZGlmZm1hdHJpeG5hbWVzW2ldLHN0YXJ0PS0xMCkpIH0KaWYoaT4xKXsKd3JpdGUueGxzeChhcy5kYXRhLmZyYW1lKGdldChkaWZmbWF0cml4bmFtZXNbaV0pKSwgZmlsZSwgc2hlZXROYW1lID0gc3RyX3N1YihkaWZmbWF0cml4bmFtZXNbaV0sc3RhcnQ9LTEwKSwgYXBwZW5kID0gVFJVRSkKfQp9CmBgYApgYGB7cn0KRGlmZk1hdHJpeCA8LSBsaXN0KCkKCmRpZmZtYXRyaXhuYW1lcyA8LSBjKCJtb2R1bGVzUmVhY3RvbWVNT0w1IiwKICAgICAgICAgICAgICAgICAgICAibW9kdWxlc1JlYWN0b21lT1BDIikKICAgICAgICAgICAgICAgICAgICAgCgpkby5jYWxsKGhlYWQsYXMubGlzdChhcy5uYW1lKGRpZmZtYXRyaXhuYW1lc1sxXSkpKQpgYGAKYGBge3J9CgpkaWZmbWF0cml4bmFtZXMgPC0gYygibW9kdWxlc1JlYWN0b21lTU9MNSIsCiAgICAgICAgICAgICAgICAgICAgIm1vZHVsZXNSZWFjdG9tZU9QQyIpCgpsaWJyYXJ5KHhsc3gpCmxpYnJhcnkoc3RyaW5ncikKc2V0d2QoIn4vRG9jdW1lbnRzL1NpbmdsZUNlbGxEYXRhL05ldHdvcmtjbHVzdGVyaW5nL0VsaXNhQW5hbHlzaXMvSW50ZWdyYXRpb25EVEFuZXR3b3JrL0ZpZ3VyZXMvIikKZmlsZSA8LSBwYXN0ZSgiUGF0aHdheWFuYWx5c2lzLnhsc3giLCBzZXAgPSAiIikKCmZvcihpIGluIDE6bGVuZ3RoKGRpZmZtYXRyaXhuYW1lcykpewppZihpPT0xKXsKd3JpdGUueGxzeChhcy5kYXRhLmZyYW1lKGdldChkaWZmbWF0cml4bmFtZXNbaV0pKSwgZmlsZSwgc2hlZXROYW1lID0gc3RyX3N1YihkaWZmbWF0cml4bmFtZXNbaV0sc3RhcnQ9LTEwKSkgfQppZihpPjEpewp3cml0ZS54bHN4KGFzLmRhdGEuZnJhbWUoZ2V0KGRpZmZtYXRyaXhuYW1lc1tpXSkpLCBmaWxlLCBzaGVldE5hbWUgPSBzdHJfc3ViKGRpZmZtYXRyaXhuYW1lc1tpXSxzdGFydD0tMTApLCBhcHBlbmQgPSBUUlVFKQp9Cn0KCnNldHdkKCJ+L0RvY3VtZW50cy9TaW5nbGVDZWxsRGF0YS9OZXR3b3JrY2x1c3RlcmluZy9FbGlzYUFuYWx5c2lzL0ludGVncmF0aW9uRFRBbmV0d29yay8iKQp3cml0ZS54bHN4KG9saWdvcy5pbnRlZ3JhdGVkLm1hcmtlcnMsZmlsZT0iRW5yaWNoZWRnZW5lc3BlcmNsdXN0ZXIueGxzeCIsIHNoZWV0TmFtZSA9ICJFbnJpY2hlZENsdXN0ZXJzV2lsY294b24iKQpgYGAKd3JpdGUueGxzeChvbGlnb3MuaW50ZWdyYXRlZC5tYXJrZXJzLCAifi9Eb2N1bWVudHMvU2luZ2xlQ2VsbERhdGEvTmV0d29ya2NsdXN0ZXJpbmcvRWxpc2FBbmFseXNpcy9JbnRlZ3JhdGlvbkRUQW5ldHdvcmsvRW5yaWNoZWRnZW5lc3BlcmNsdXN0ZXIueGxzeCIsIHNoZWV0TmFtZSA9ICJFbnJpY2hlZENsdXN0ZXJzV2lsY294b24iKQoKICAKICAKICAjIyBTb21lIGludGVycHJldGF0aW9uczoKICAKICBUaGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIHRoZSBTVFJJTkcgYW5hbHlzaXMgcHJlZGljdCB0aGF0IHRoZSBtYWpvciBwYXRod2F5cyBzZWVtIHRvIGJlIHRoZSBMMWNhbSBwYXRod2F5IHNlZSBbcGxvdF0oI2wxY2FtKSBhbmQgYW50ZXJvZ3JhZGUvcmV0cm9ncmFkZSB0cmFuc3BvcnQuICAKRnVydGhlcm1vcmUsIHRoZSBwbG90dGluZyBvZiB0aGUgRFRBIGdlbmVzIG9uIHRoZSBTY2llbmNlMjAxNiBkYXRhc2V0IGNsZWFybHkgc2hvdyB0aGF0IG1vc3Qgb2YgdGhlIERUQSBnZW5lcyBhcmUgYmVoYXZpbmcgZGlmZmVyZW50bHksIHdoZXJlIG1vc3Qgb2YgdGhlIERUQSBnZW5lcyBhcmUgZWl0aGVyIHVwcmVndWxhdGVkIG9yIGRvd25yZWd1bGF0ZWQgdXBvbiBkaWZmZXJlbnRpYXRpb24sIG9yIG1haW50YWluZWQgdGhyb3VnaG91dCBkaWZmZXJlbnRpYXRpb24sIHdoZXJlYXMgaW4gdGhlIERUQSBkYXRhc2V0IHdlIHNlZSB2YXJpYXRpb24gaW4gdGhlIE1PTHMuCiAgCkkgcHV0IHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgaW4gdGhlIFNUUklOR2RiIHZpc3VhbGl6ZXIgdG8gbWFrZSBhIG5ldHdvcmsgb2YgcG9zc2libGUgYXNzb2NpYXRpb25zLiAgCkkgZG9uJ3Qga25vdyBmb3IgaG93IGxvbmcgdGhlc2Ugam9icyB3aWxsIHJlbWFpbiBvbiB0aGUgU1RSSU5HIHNlcnZlciBidXQgZm9yIHRoZSBTVFJJTkcgYW5hbHlzaXMgb2YgT0wgRFRBIGdlbmVzIHNlZSA8aHR0cHM6Ly9zdHJpbmctZGIub3JnL2NnaS9uZXR3b3JrLnBsP3Rhc2tJZD00ZnhpN2N5UG8zWXA+ICBhbmQgZm9yIHRoZSBPUEMgZ2VuZXMgc2VlIDxodHRwczovL3N0cmluZy1kYi5vcmcvY2dpL25ldHdvcmsucGw/dGFza0lkPVg5eFZyaTA2dWhYRD4KICAKTWFueSBnZW5lcyBhcmUgYWZmZWN0ZWQgYnkgdGhlIERUQS4gRmlyc3Qgb2YgYWxsIHRoZXJlIGlzIGRvd25yZWd1bGF0aW9uIG9mIGEgbnVtYmVyIG9mIGdlbmVzLCAKCi0gdGhlIG9uZXMgYXNzb2NpYXRlZCB3aXRoIEwxLWNhbSBpbnRlcmFjdGlvbnMgc3VjaCBhcyBORkFTQywgQW5reXJpbiAoQW5rMi8zKSwgVHViMWExLCBEcHlzbDIuIEFua3lyaW4gY2FuIGJpbmQgTkZBU0MsIGFuZCBpcyBib3VuZCB0byBtaWNyb3R1YnVsZXMsIHdpdGhpbiBwYXJhbm9kZXMsIGFuZCBwZXJoYXBzIG90aGVyIHJlZ2lvbnMgb2YgdGhlIHNoZWF0aD8KLSBGcm9tIHRoZSBTVFJJTkcgYW5hbHlzaXMgaXQgc2VlbXMgdGhhdCBhIGJyb2FkIHNldCBvZiBnZW5lcyBhcmUgZG93bnJlZ3VsYXRlZCBpbnZvbHZlZCBpbiBtaWNyb3R1YnVsZSBhc3NlbWJseSAoTWFwdCxBUFApIAotIE1pY3JvdHVidWxlIHBvbHltZXJpemF0aW9uIChUcHBwKQotIE1pdG9jaG9uZHJpYWwgdHJhbnNwb3J0IChLaWYxYiAqbWljcm90dWJ1bGUgcGx1cyBkaXJlY3RlZCogb25seSB0byB0aGUgb3V0c2lkZSBvZiB0aGUgY2VsbC9wcm9jZXNzLCBJRiBwb2xhcml0eSBpcyBtYWludGFpbmVkKSAKLSB2ZXNpY2xlIGZvcm1hdGlvbiAoU3YyYSxOZmlhLEFiY2EyLE5wYzEpLGZsaXBwYXNlKEF0cDhhMSkKLSB2ZXNpY2xlIHRyYW5zcG9ydC9taWNyb3R1YnVsZSB0cmFuc3BvcnQgKER5bmMxaDEsS2lmMWIpLENlbGwtanVuY3Rpb24gYW5kIG1lbWJyYW5lIGNvbXBsZXggKEFuazIvMyxEc3QsTmZhc2MsQ250bjIgZXRjKQotIFJOQS1iaW5kaW5nL3RyYW5zbGF0aW9uIGluaXRpYXRpb24gKFFrMSxNeXJmKQotIE15ZWxpbiBjb25zdGl0dWVudHMgYW5kIG15ZWxpbmF0aW9uIChVZ3Q4YSxNeXJmKQotIFRyYW5zbGF0aW9uIGluaXRpYXRpb24gYW5kIE1pdG9jaG9uZHJpYWwgcmVjcnVpdG1lbnQgCi0gYW5kIG1vcmUuICAKClRvIG1ha2Ugc29tZSBzZW5zZSBvZiB0aGlzIEkgaGF2ZSBhbm5vdGF0ZWQgdGhlIFNUUklOR2RiIG5ldHdvcmsgd2l0aCB1cC9kb3duIHJlZ3VsYXRpb24uIFJlZCBtZWFucyBkb3duIGluIERUQSBhbmQgZ3JlZW4gdXAgaW4gRFRBLgoKICAKIVtTVFJJTkdkYiBuZXR3b3JrIG9mIE1PTCBEVEEgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzXShzdHJpbmdfaGlyZXNfaW1hZ2VfRGlmZnRvcDEwVSZMLmpwZykgIApPbmUgb2YgdGhlIG1ham9yIGdlbmVzIGRvd25yZWd1bGF0ZWQgaW4gdGhlIERUQSBpcyBUcHBwLiBBIHJlY2VudCBwYXBlciBjYW1lIG91dCB0aGF0IGltcGxpY2F0ZXMgVHBwcCBpbiBmb3JtYXRpb24gb2YgR29sZ2kgb3V0cG9zdHMsIFtwYXBlcl0oaHR0cHM6Ly93d3cubWVuZ21lbmdmdS5jb20vdXBsb2Fkcy8xLzIvNi84LzEyNjg4MzI1Mi9mdV8yMDE5Xy1fdHBwcF9nb2xnaV9vdXRwb3N0cy5wZGYpIC4gICAKVGhleSBzaG93IHRoYXQgYW50ZXJvZ3JhZGUgYW5kIHJldHJvZ3JhZGUgdHJhbnNwb3J0IG1pZ2h0IGJlIGVzdGFibGlzaGVkIHRocm91Z2ggdGhlIGZvcm1hdGlvbiBvZiBnb2xnaS1vdXRwb3N0cywgd2hlcmUgbWljcm90dWJ1bGUgbnVjbGVhdGlvbiBpcyBpbiBwYXJ0IGluaXRpYXRlZCBieSBUUFBQIChldmVuIHRob3VnaCB0aGV5IGNhbiBzZWUgY29tcGVuc2F0aW9uIHVwb24gS08pLiAgClRoZSBLTyBvZiBUUFBQIGFmZmVjdHMgdGhlIG1pY3JvdHVidWxlLWJyYW5jaGluZywgbWljcm8tdHVidWxlIHBvbGFyaXR5LCBhbmQgS08gZ2VuZXJhdGVzIHRoaWNrZXIgYmFzZSBwcm9jZXNzZXMgd2l0aCB0aGlubmVyIGFuZCBsZXNzIGV4dGVuZGVkIHByb2Nlc3Nlcy4KClRoZSBEVEEgZG93bnJlZ3VsYXRlZCBnZW5lcyBzdHJvbmdseSBzdWdnZXN0IHRoYXQgc29tZXdoZXJlIHVwc3RyZWFtIG9yIGRvd25zdHJlYW0gb2YgbWljcm90dWJ1bGUgbnVjbGVhdGlvbiwgZ29sZ2ktb3V0cG9zdCBmb3JtYXRpb24sIG9yIHByb3BlciBwb2xhcml0eSBzZXQtdXAgb2YgdGhlIG1pY3JvdHVidWxlIHN0cmFuZHMgc29tZXRoaW5nIGlzIGdvaW5nIHdyb25nLi4uICAgIApBZGRpdGlvbmFsbHksIEkgc2VlIGRvd25yZWd1bGF0aW9uIG9mIEFQUCBhbmQgTWFwdCBpbiBEVEEgT0wgYW5kIE9QQy4gICAKVGhlIFNUUklORyBhbmFseXNpcyBnaXZlcyBtdWNoIHdlaWdodCB0byB0aGUgQVBQIHByb3RlaW4gYW5kIHB1dHMgaXQgdmVyeSBjZW50cmFsIG9uIHRoZSBuZXR3b3JrLCBhbHNvLCBpdCBpcyBwcmVkaWN0ZWQgdG8gYmUgcGFydCBvZiBtYW55IHBhdGh3YXkgZW5yaWNobWVudHMgaW4gdGhlIFNUUklORyBhbmFseXNpcyBvZiB0aGUgbmV0d29yay4gIApJIGZlYXIgdGhhdCB0aGlzIG1pZ2h0IGJlIGJlY2F1c2UgdGhlcmUgaXMgYSBiaWFzIGR1ZSB0byB0aGUgaGVhdmlseSByZXNlYXJjaGVkIG5hdHVyZSB0aGUgQW15bG9pZC1iZXRhIEE0IHByb3RlaW4sIGhvd2V2ZXIgaXQgaGFzIGJlZW4gc2hvd24gdGhhdCBBUFAgY2FuIGFjdCBhcyBhIGNhcmdvLXJlY2VwdG9yIGZvciBhbnRlcm9ncmFkZSBtb3RvciByZWNlcHRvcnMgIApbcGFwZXIxXShodHRwczovL2RvaS5vcmcvMTAuMTA3MyUyRnBuYXMuMDYwNzUyNzEwMyksIFtwYXBlcjJdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDg4JTJGMTQ3OC0zOTc1JTJGOSUyRjUlMkYwNTUwMDUpLCBpbmRpY2F0aW5nIGEgbW9yZSBjbGVhciByb2xlIGludG8gd2hhdCB0aGlzIGFuYWx5c2lzIGlzIHN1Z2dlc3RpbmcuICAKICAKClRvIG1lLCBpdCBzZWVtcyB0aGF0IG1vc3QgbGlrZWx5IHRoZSBwb2xhcml0eSBvZiB0aGUgbWljcm90dWJ1bGVzIGluIHRoZSBwcm9jZXNzZXMgYW5kIHRoZSBhbnRlcm9ncmFkZSwgb3Igb3V0d2FyZCwgYnV0IGFsc28gcmV0cm9ncmFkZSB0cmFuc3BvcnQgaXMgbGVzcyBlZmZpY2llbnQgdGhhbiBub3JtYWwsIGFuZCB0aGF0IG1vc3Qgb2YgdGhlIG9ic2VydmVkIGRpZmZlcmVudGlhbGx5IHVwIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzIGFyZSBhIGF0dGVtcHQgb2YgdGhlIGNlbGwgdG8gY29tcGVuc2F0ZSBmb3IgdGhpcyBsYWNrIG9mIHRyYW5zcG9ydC4gIApJIG5vdGljZWQgYW4gaW5jcmVhc2UgaW4gY2hhcGVyb25lIGFjdGl2aXR5L3ViaXF1aXRpbiBsaWdhc2UgYWN0aXZpdHkgZ2VuZSBleHByZXNzaW9uLCBhbmQgYW4gdXByZWd1bGF0aW9uIG9mIGx5c29zb21hbCBnZW5lcyAoTGFtcDEvMiksIHBlcmhhcHMgYWxzbyBpbmRpY2F0aW5nIGNvbXBlbnNhdGlvbiBvZiBkZWZlY3RpdmUgdHJhbnNwb3J0IGFuZCBtaWNyb3R1YnVsZSBhc3NlbWJseS4gIApBZGRpdGlvbmFsbHksIG1pdG9jaG9uZHJpYWwgdHJhbnNwb3J0IHByb3RlaW5zIGFuZCBtaXRvY2hvbmRyaWFsIHJlcGlyYXRvcnkgZWxlY3Ryb24gdHJhbnNwb3J0IGFuZCBBVFAgc3ludGhlc2lzIHNlZW0gdG8gYmUgZG93bnJlZ3VsYXRlZCBhcyB3ZWxsLgoKCkJhc2VkIG9uIHRoZSBhYm92ZSwgaWYgaW5zaWRlIHRoZSBzY29wZSBvZiB0aGUgcGFwZXIsIGFuZCB0byBnZXQgc29tZSBoaW50cyBhcyB0byBpZiB0aGlzIGFuYWx5c2lzIGhhcyBhbnkgbWVyaXQsIEkgd291bGQgbGlrZSB0byBzdWdnZXN0IHNvbWUgc3RlcHMgdGhhdCBtaWdodCBpbmRpY2F0ZSB3aGF0IGlzIGdvaW5nIHdyb25nLgoKLSBWaXN1YWxpemF0aW9uIG9mIHZlc2ljbGUgbnVtYmVyCi0gU3RhaW5pbmcgb2YgZ29sZ2ktb3V0cG9zdHMgaW4gdGhlIHByb2Nlc3Nlcy8gVHBwcCBzdGFpbmluZyBpbiB0aGUgT0xzIChzaG91bGQgcGVyaGFwcyBiZSBkb3duIGluIERUQSkKLSBOdW1iZXIgb2YgbWl0b2Nob25kcmlhIGluIHByb2Nlc3NlcyAoaXMgaXQgcG9zc2libGUgdG8gY291bnQgZWFzaWx5PykKLSBNaWNyb3R1YnVsZSBwb2xhcml0eSBtZWFzdXJlbWVudHMgc3VjaCBhcyBpbiB0aGUgVHBwcCBwYXBlciB3aGVyZSB0aGV5IHNob3cgdGhhdCBwbHVzLWVuZCBvdXQgcG9sYXJpdHkgZHJvcHMgZnJvbSA4MCUgdG8gNTAlIHVzaW5nIEVCMy1FR0ZQIGFuIHBsdXMgZW5kIGFzc29jaWF0aW5nIHByb3RlaW4uCi0gTWF5YmUgdGhlcmUgaXMgZXZpZGVuY2UgZm9yIGFiYmVyYW50IGJyYW5jaGluZyBpbiB0aGUgRFRBIGNvbXBlbnNhdGVkIE9Mcz8KCgpJIHdvdWxkIGxvdmUgdG8gaGVhciB5b3VyIHRob3VnaHRzIG9uIHRoaXMuCgovRGF2aWQKCiFbUFM6IEkgYWxzbyBhbmFseXNlZCB0aGUgT1BDIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9uIFNUUklOR10oc3RyaW5nX2hpcmVzX2ltYWdlX0RpZmZPUEMuanBnKQoKCiAgCiAg